diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 8a4353ef930..1931b77b494 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -599,7 +599,7 @@ build-tools-and-docs-debian: optional: true variables: IMAGE: debian-amd64 - MAKE_CHECK_ARGS: check-unit check-softfloat ctags TAGS cscope + MAKE_CHECK_ARGS: check-unit ctags TAGS cscope CONFIGURE_ARGS: --disable-system --disable-user --enable-docs --enable-tools QEMU_JOB_PUBLISH: 1 artifacts: diff --git a/.gitlab-ci.d/cirrus/freebsd-12.vars b/.gitlab-ci.d/cirrus/freebsd-12.vars index b4842271b20..f59263731f4 100644 --- a/.gitlab-ci.d/cirrus/freebsd-12.vars +++ b/.gitlab-ci.d/cirrus/freebsd-12.vars @@ -1,4 +1,5 @@ # THIS FILE WAS AUTO-GENERATED +# ... and then edited to fix py39, pending proper lcitool update. # # $ lcitool variables freebsd-12 qemu # @@ -11,6 +12,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-virtualenv py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars index 546a82dd756..40fc961398f 100644 --- a/.gitlab-ci.d/cirrus/freebsd-13.vars +++ b/.gitlab-ci.d/cirrus/freebsd-13.vars @@ -1,4 +1,5 @@ # THIS FILE WAS AUTO-GENERATED +# ... and then edited to fix py39, pending proper lcitool update. # # $ lcitool variables freebsd-13 qemu # @@ -11,6 +12,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-virtualenv py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml index 4f292a8a5b1..03e74c97dbc 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml @@ -8,8 +8,6 @@ ubuntu-20.04-s390x-all-linux-static: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" @@ -29,8 +27,7 @@ ubuntu-20.04-s390x-all: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 + timeout: 75m rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" @@ -47,8 +44,6 @@ ubuntu-20.04-s390x-alldbg: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -70,8 +65,6 @@ ubuntu-20.04-s390x-clang: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -92,8 +85,6 @@ ubuntu-20.04-s390x-tci: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -113,8 +104,6 @@ ubuntu-20.04-s390x-notcg: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual diff --git a/.gitlab-ci.d/edk2.yml b/.gitlab-ci.d/edk2.yml index 13d0f8b019f..314e1017452 100644 --- a/.gitlab-ci.d/edk2.yml +++ b/.gitlab-ci.d/edk2.yml @@ -1,60 +1,85 @@ # All jobs needing docker-edk2 must use the same rules it uses. .edk2_job_rules: - rules: # Only run this job when ... - - changes: - # this file is modified - - .gitlab-ci.d/edk2.yml - # or the Dockerfile is modified - - .gitlab-ci.d/edk2/Dockerfile - # or roms/edk2/ is modified (submodule updated) - - roms/edk2/* - when: on_success - - if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2' - when: on_success - - if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2' - when: on_success + rules: + # Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set + - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"' + when: never + + # In forks, if QEMU_CI=1 is set, then create manual job + # if any of the files affecting the build are touched + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project"' + changes: + - .gitlab-ci.d/edk2.yml + - .gitlab-ci.d/edk2/Dockerfile + - roms/edk2/* + when: manual + + # In forks, if QEMU_CI=1 is set, then create manual job + # if the branch/tag starts with 'edk2' + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_REF_NAME =~ /^edk2/' + when: manual + + # In forks, if QEMU_CI=1 is set, then create manual job + # if last commit msg contains 'EDK2' (case insensitive) + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_MESSAGE =~ /edk2/i' + when: manual + + # Run if any files affecting the build output are touched + - changes: + - .gitlab-ci.d/edk2.yml + - .gitlab-ci.d/edk2/Dockerfile + - roms/edk2/* + when: on_success + + # Run if the branch/tag starts with 'edk2' + - if: '$CI_COMMIT_REF_NAME =~ /^edk2/' + when: on_success + + # Run if last commit msg contains 'EDK2' (case insensitive) + - if: '$CI_COMMIT_MESSAGE =~ /edk2/i' + when: on_success docker-edk2: - extends: .edk2_job_rules - stage: containers - image: docker:19.03.1 - services: - - docker:19.03.1-dind - variables: - GIT_DEPTH: 3 - IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build - # We don't use TLS - DOCKER_HOST: tcp://docker:2375 - DOCKER_TLS_CERTDIR: "" - before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - script: - - docker pull $IMAGE_TAG || true - - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - --tag $IMAGE_TAG .gitlab-ci.d/edk2 - - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - - docker push $IMAGE_TAG + extends: .edk2_job_rules + stage: containers + image: docker:19.03.1 + services: + - docker:19.03.1-dind + variables: + GIT_DEPTH: 3 + IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build + # We don't use TLS + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - docker pull $IMAGE_TAG || true + - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + --tag $IMAGE_TAG .gitlab-ci.d/edk2 + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + - docker push $IMAGE_TAG build-edk2: - extends: .edk2_job_rules - stage: build - needs: ['docker-edk2'] - artifacts: - paths: # 'artifacts.zip' will contains the following files: - - pc-bios/edk2*bz2 - - pc-bios/edk2-licenses.txt - - edk2-stdout.log - - edk2-stderr.log - image: $CI_REGISTRY_IMAGE:edk2-cross-build - variables: - GIT_DEPTH: 3 - script: # Clone the required submodules and build EDK2 - - git submodule update --init roms/edk2 - - git -C roms/edk2 submodule update --init -- - ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 - BaseTools/Source/C/BrotliCompress/brotli - CryptoPkg/Library/OpensslLib/openssl - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli - - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) - - echo "=== Using ${JOBS} simultaneous jobs ===" - - make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2 + extends: .edk2_job_rules + stage: build + needs: ['docker-edk2'] + artifacts: + paths: # 'artifacts.zip' will contains the following files: + - pc-bios/edk2*bz2 + - pc-bios/edk2-licenses.txt + - edk2-stdout.log + - edk2-stderr.log + image: $CI_REGISTRY_IMAGE:edk2-cross-build + variables: + GIT_DEPTH: 3 + script: # Clone the required submodules and build EDK2 + - git submodule update --init roms/edk2 + - git -C roms/edk2 submodule update --init -- + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 + BaseTools/Source/C/BrotliCompress/brotli + CryptoPkg/Library/OpensslLib/openssl + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli + - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - echo "=== Using ${JOBS} simultaneous jobs ===" + - make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2 diff --git a/.gitlab-ci.d/opensbi.yml b/.gitlab-ci.d/opensbi.yml index 29a22930d1d..04ed5a3ea13 100644 --- a/.gitlab-ci.d/opensbi.yml +++ b/.gitlab-ci.d/opensbi.yml @@ -1,61 +1,85 @@ # All jobs needing docker-opensbi must use the same rules it uses. .opensbi_job_rules: - rules: # Only run this job when ... - - changes: - # this file is modified - - .gitlab-ci.d/opensbi.yml - # or the Dockerfile is modified - - .gitlab-ci.d/opensbi/Dockerfile - when: on_success - - changes: # or roms/opensbi/ is modified (submodule updated) - - roms/opensbi/* - when: on_success - - if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi' - when: on_success - - if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI' - when: on_success + rules: + # Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set + - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"' + when: never + + # In forks, if QEMU_CI=1 is set, then create manual job + # if any files affecting the build output are touched + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project"' + changes: + - .gitlab-ci.d/opensbi.yml + - .gitlab-ci.d/opensbi/Dockerfile + - roms/opensbi/* + when: manual + + # In forks, if QEMU_CI=1 is set, then create manual job + # if the branch/tag starts with 'opensbi' + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_REF_NAME =~ /^opensbi/' + when: manual + + # In forks, if QEMU_CI=1 is set, then create manual job + # if the last commit msg contains 'OpenSBI' (case insensitive) + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_MESSAGE =~ /opensbi/i' + when: manual + + # Run if any files affecting the build output are touched + - changes: + - .gitlab-ci.d/opensbi.yml + - .gitlab-ci.d/opensbi/Dockerfile + - roms/opensbi/* + when: on_success + + # Run if the branch/tag starts with 'opensbi' + - if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' + when: on_success + + # Run if the last commit msg contains 'OpenSBI' (case insensitive) + - if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' + when: on_success docker-opensbi: - extends: .opensbi_job_rules - stage: containers - image: docker:19.03.1 - services: - - docker:19.03.1-dind - variables: - GIT_DEPTH: 3 - IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build - # We don't use TLS - DOCKER_HOST: tcp://docker:2375 - DOCKER_TLS_CERTDIR: "" - before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - script: - - docker pull $IMAGE_TAG || true - - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - --tag $IMAGE_TAG .gitlab-ci.d/opensbi - - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - - docker push $IMAGE_TAG + extends: .opensbi_job_rules + stage: containers + image: docker:19.03.1 + services: + - docker:19.03.1-dind + variables: + GIT_DEPTH: 3 + IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build + # We don't use TLS + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - docker pull $IMAGE_TAG || true + - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + --tag $IMAGE_TAG .gitlab-ci.d/opensbi + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + - docker push $IMAGE_TAG build-opensbi: - extends: .opensbi_job_rules - stage: build - needs: ['docker-opensbi'] - artifacts: - paths: # 'artifacts.zip' will contains the following files: - - pc-bios/opensbi-riscv32-generic-fw_dynamic.bin - - pc-bios/opensbi-riscv64-generic-fw_dynamic.bin - - opensbi32-generic-stdout.log - - opensbi32-generic-stderr.log - - opensbi64-generic-stdout.log - - opensbi64-generic-stderr.log - image: $CI_REGISTRY_IMAGE:opensbi-cross-build - variables: - GIT_DEPTH: 3 - script: # Clone the required submodules and build OpenSBI - - git submodule update --init roms/opensbi - - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) - - echo "=== Using ${JOBS} simultaneous jobs ===" - - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi32-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2 - - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2 + extends: .opensbi_job_rules + stage: build + needs: ['docker-opensbi'] + artifacts: + paths: # 'artifacts.zip' will contains the following files: + - pc-bios/opensbi-riscv32-generic-fw_dynamic.bin + - pc-bios/opensbi-riscv64-generic-fw_dynamic.bin + - opensbi32-generic-stdout.log + - opensbi32-generic-stderr.log + - opensbi64-generic-stdout.log + - opensbi64-generic-stderr.log + image: $CI_REGISTRY_IMAGE:opensbi-cross-build + variables: + GIT_DEPTH: 3 + script: # Clone the required submodules and build OpenSBI + - git submodule update --init roms/opensbi + - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - echo "=== Using ${JOBS} simultaneous jobs ===" + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi32-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2 diff --git a/.travis.yml b/.travis.yml index 9afc4a54b8f..fb3baabca9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -218,12 +218,11 @@ jobs: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" - UNRELIABLE=true - - DFLTCC=0 script: - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? - | if [ "$BUILD_RC" -eq 0 ] ; then - mv pc-bios/s390-ccw/*.img pc-bios/ ; + mv pc-bios/s390-ccw/*.img qemu-bundle/usr/local/share/qemu ; ${TEST_CMD} ; else $(exit $BUILD_RC); @@ -258,7 +257,7 @@ jobs: env: - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - - DFLTCC=0 + - name: "[s390x] GCC (user)" arch: s390x dist: focal @@ -270,7 +269,7 @@ jobs: - ninja-build env: - CONFIG="--disable-containers --disable-system" - - DFLTCC=0 + - name: "[s390x] Clang (disable-tcg)" arch: s390x dist: focal @@ -304,4 +303,3 @@ jobs: - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools --host-cc=clang --cxx=clang++" - UNRELIABLE=true - - DFLTCC=0 diff --git a/MAINTAINERS b/MAINTAINERS index aaa649a50d1..6af9cd985ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -165,8 +165,6 @@ F: tests/qtest/arm-cpu-features.c F: hw/arm/ F: hw/cpu/a*mpcore.c F: include/hw/cpu/a*mpcore.h -F: disas/arm-a64.cc -F: disas/libvixl/ F: docs/system/target-arm.rst F: docs/system/arm/cpu-features.rst @@ -1067,6 +1065,7 @@ F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst F: tests/qtest/*aspeed* +F: hw/arm/fby35.c NRF51 M: Joel Stanley @@ -1840,7 +1839,6 @@ R: Ani Sinha S: Supported F: include/hw/acpi/* F: include/hw/firmware/smbios.h -F: hw/mem/* F: hw/acpi/* F: hw/smbios/* F: hw/i386/acpi-build.[hc] @@ -1851,6 +1849,7 @@ F: tests/qtest/acpi-utils.[hc] F: tests/data/acpi/ F: docs/specs/acpi_cpu_hotplug.rst F: docs/specs/acpi_mem_hotplug.rst +F: docs/specs/acpi_nvdimm.rst F: docs/specs/acpi_pci_hotplug.rst F: docs/specs/acpi_hw_reduced_hotplug.rst @@ -2158,15 +2157,6 @@ F: qapi/rocker.json F: tests/rocker/ F: docs/specs/rocker.txt -NVDIMM -M: Xiao Guangrong -S: Maintained -F: hw/acpi/nvdimm.c -F: hw/mem/nvdimm.c -F: include/hw/mem/nvdimm.h -F: docs/nvdimm.txt -F: docs/specs/acpi_nvdimm.rst - e1000x M: Dmitry Fleytman S: Maintained @@ -2588,6 +2578,7 @@ M: Ben Widawsky M: Jonathan Cameron S: Supported F: hw/cxl/ +F: hw/mem/cxl_type3.c F: include/hw/cxl/ Dirty Bitmaps @@ -2704,6 +2695,19 @@ F: softmmu/physmem.c F: include/exec/memory-internal.h F: scripts/coccinelle/memory-region-housekeeping.cocci +Memory devices +M: David Hildenbrand +M: Igor Mammedov +R: Xiao Guangrong +S: Supported +F: hw/mem/memory-device.c +F: hw/mem/nvdimm.c +F: hw/mem/pc-dimm.c +F: include/hw/mem/memory-device.h +F: include/hw/mem/nvdimm.h +F: include/hw/mem/pc-dimm.h +F: docs/nvdimm.txt + SPICE M: Gerd Hoffmann S: Odd Fixes @@ -2746,6 +2750,7 @@ F: softmmu/cpu-throttle.c F: softmmu/cpu-timers.c F: softmmu/icount.c F: softmmu/runstate-action.c +F: softmmu/runstate.c F: qapi/run-state.json Read, Copy, Update (RCU) @@ -2876,6 +2881,7 @@ T: git https://repo.or.cz/qemu/armbru.git qapi-next QEMU Guest Agent M: Michael Roth +M: Konstantin Kostiuk S: Maintained F: qga/ F: docs/interop/qemu-ga.rst @@ -3307,8 +3313,6 @@ M: Richard Henderson S: Maintained L: qemu-arm@nongnu.org F: tcg/aarch64/ -F: disas/arm-a64.cc -F: disas/libvixl/ ARM TCG target M: Richard Henderson @@ -3580,6 +3584,8 @@ M: Coiby Xu S: Maintained F: block/export/vhost-user-blk-server.c F: block/export/vhost-user-blk-server.h +F: block/export/virtio-blk-handler.c +F: block/export/virtio-blk-handler.h F: include/qemu/vhost-user-server.h F: tests/qtest/libqos/vhost-user-blk.c F: tests/qtest/libqos/vhost-user-blk.h @@ -3592,6 +3598,13 @@ L: qemu-block@nongnu.org S: Supported F: block/export/fuse.c +VDUSE library and block device exports +M: Xie Yongji +S: Maintained +F: subprojects/libvduse/ +F: block/export/vduse-blk.c +F: block/export/vduse-blk.h + Replication M: Wen Congyang M: Xie Changlong diff --git a/Makefile b/Makefile index 3c0d89057eb..13234f2aa43 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ x := $(shell rm -rf meson-private meson-info meson-logs) endif # 1. ensure config-host.mak is up-to-date -config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/VERSION @echo config-host.mak is out-of-date, running configure @if test -f meson-private/coredata.dat; then \ ./config.status --skip-meson; \ @@ -186,16 +186,14 @@ include $(SRC_PATH)/tests/Makefile.include all: recurse-all -ROM_DIRS = $(addprefix pc-bios/, $(ROMS)) -ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS))) -# Only keep -O and -g cflags -.PHONY: $(ROM_DIRS_RULES) -$(ROM_DIRS_RULES): +ROMS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROMS))) +.PHONY: $(ROMS_RULES) +$(ROMS_RULES): $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) .PHONY: recurse-all recurse-clean -recurse-all: $(addsuffix /all, $(ROM_DIRS)) -recurse-clean: $(addsuffix /clean, $(ROM_DIRS)) +recurse-all: $(addsuffix /all, $(ROMS)) +recurse-clean: $(addsuffix /clean, $(ROMS)) ###################################################################### @@ -218,7 +216,7 @@ qemu-%.tar.bz2: distclean: clean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : - rm -f config-host.mak + rm -f config-host.mak qemu-bundle rm -f tests/tcg/config-*.mak rm -f config.status rm -f roms/seabios/config.mak diff --git a/accel/accel-common.c b/accel/accel-common.c index 7b8ec7e0f72..50035bda55d 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -49,6 +49,14 @@ AccelClass *accel_find(const char *opt_name) return ac; } +/* Return the name of the current accelerator */ +const char *current_accel_name(void) +{ + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + + return ac->name; +} + static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque) { CPUClass *cc = CPU_CLASS(klass); diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a4c4863f532..99aede73b7c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -45,8 +45,10 @@ #include "qemu/guest-random.h" #include "sysemu/hw_accel.h" #include "kvm-cpus.h" +#include "sysemu/dirtylimit.h" #include "hw/boards.h" +#include "monitor/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD @@ -476,6 +478,7 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) cpu->kvm_state = s; cpu->vcpu_dirty = true; cpu->dirty_pages = 0; + cpu->throttle_us_per_full = 0; mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { @@ -756,17 +759,20 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu) } /* Must be with slots_lock held */ -static uint64_t kvm_dirty_ring_reap_locked(KVMState *s) +static uint64_t kvm_dirty_ring_reap_locked(KVMState *s, CPUState* cpu) { int ret; - CPUState *cpu; uint64_t total = 0; int64_t stamp; stamp = get_clock(); - CPU_FOREACH(cpu) { - total += kvm_dirty_ring_reap_one(s, cpu); + if (cpu) { + total = kvm_dirty_ring_reap_one(s, cpu); + } else { + CPU_FOREACH(cpu) { + total += kvm_dirty_ring_reap_one(s, cpu); + } } if (total) { @@ -787,7 +793,7 @@ static uint64_t kvm_dirty_ring_reap_locked(KVMState *s) * Currently for simplicity, we must hold BQL before calling this. We can * consider to drop the BQL if we're clear with all the race conditions. */ -static uint64_t kvm_dirty_ring_reap(KVMState *s) +static uint64_t kvm_dirty_ring_reap(KVMState *s, CPUState *cpu) { uint64_t total; @@ -807,7 +813,7 @@ static uint64_t kvm_dirty_ring_reap(KVMState *s) * reset below. */ kvm_slots_lock(); - total = kvm_dirty_ring_reap_locked(s); + total = kvm_dirty_ring_reap_locked(s, cpu); kvm_slots_unlock(); return total; @@ -854,7 +860,7 @@ static void kvm_dirty_ring_flush(void) * vcpus out in a synchronous way. */ kvm_cpu_synchronize_kick_all(); - kvm_dirty_ring_reap(kvm_state); + kvm_dirty_ring_reap(kvm_state, NULL); trace_kvm_dirty_ring_flush(1); } @@ -1398,7 +1404,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, * Not easy. Let's cross the fingers until it's fixed. */ if (kvm_state->kvm_dirty_ring_size) { - kvm_dirty_ring_reap_locked(kvm_state); + kvm_dirty_ring_reap_locked(kvm_state, NULL); } else { kvm_slot_get_dirty_log(kvm_state, mem); } @@ -1466,11 +1472,16 @@ static void *kvm_dirty_ring_reaper_thread(void *data) */ sleep(1); + /* keep sleeping so that dirtylimit not be interfered by reaper */ + if (dirtylimit_in_service()) { + continue; + } + trace_kvm_dirty_ring_reaper("wakeup"); r->reaper_state = KVM_DIRTY_RING_REAPER_REAPING; qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(s); + kvm_dirty_ring_reap(s, NULL); qemu_mutex_unlock_iothread(); r->reaper_iteration++; @@ -2310,6 +2321,15 @@ bool kvm_dirty_ring_enabled(void) return kvm_state->kvm_dirty_ring_size ? true : false; } +static void query_stats_cb(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp); +static void query_stats_schemas_cb(StatsSchemaList **result, Error **errp); + +uint32_t kvm_dirty_ring_size(void) +{ + return kvm_state->kvm_dirty_ring_size; +} + static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -2638,6 +2658,11 @@ static int kvm_init(MachineState *ms) } } + if (kvm_check_extension(kvm_state, KVM_CAP_BINARY_STATS_FD)) { + add_stats_callbacks(STATS_PROVIDER_KVM, query_stats_cb, + query_stats_schemas_cb); + } + return 0; err: @@ -2957,8 +2982,19 @@ int kvm_cpu_exec(CPUState *cpu) */ trace_kvm_dirty_ring_full(cpu->cpu_index); qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(kvm_state); + /* + * We throttle vCPU by making it sleep once it exit from kernel + * due to dirty ring full. In the dirtylimit scenario, reaping + * all vCPUs after a single vCPU dirty ring get full result in + * the miss of sleep, so just reap the ring-fulled vCPU. + */ + if (dirtylimit_in_service()) { + kvm_dirty_ring_reap(kvm_state, cpu); + } else { + kvm_dirty_ring_reap(kvm_state, NULL); + } qemu_mutex_unlock_iothread(); + dirtylimit_vcpu_execute(cpu); ret = 0; break; case KVM_EXIT_SYSTEM_EVENT: @@ -3697,3 +3733,405 @@ static void kvm_type_init(void) } type_init(kvm_type_init); + +typedef struct StatsArgs { + union StatsResultsType { + StatsResultList **stats; + StatsSchemaList **schema; + } result; + strList *names; + Error **errp; +} StatsArgs; + +static StatsList *add_kvmstat_entry(struct kvm_stats_desc *pdesc, + uint64_t *stats_data, + StatsList *stats_list, + Error **errp) +{ + + Stats *stats; + uint64List *val_list = NULL; + + /* Only add stats that we understand. */ + switch (pdesc->flags & KVM_STATS_TYPE_MASK) { + case KVM_STATS_TYPE_CUMULATIVE: + case KVM_STATS_TYPE_INSTANT: + case KVM_STATS_TYPE_PEAK: + case KVM_STATS_TYPE_LINEAR_HIST: + case KVM_STATS_TYPE_LOG_HIST: + break; + default: + return stats_list; + } + + switch (pdesc->flags & KVM_STATS_UNIT_MASK) { + case KVM_STATS_UNIT_NONE: + case KVM_STATS_UNIT_BYTES: + case KVM_STATS_UNIT_CYCLES: + case KVM_STATS_UNIT_SECONDS: + case KVM_STATS_UNIT_BOOLEAN: + break; + default: + return stats_list; + } + + switch (pdesc->flags & KVM_STATS_BASE_MASK) { + case KVM_STATS_BASE_POW10: + case KVM_STATS_BASE_POW2: + break; + default: + return stats_list; + } + + /* Alloc and populate data list */ + stats = g_new0(Stats, 1); + stats->name = g_strdup(pdesc->name); + stats->value = g_new0(StatsValue, 1);; + + if ((pdesc->flags & KVM_STATS_UNIT_MASK) == KVM_STATS_UNIT_BOOLEAN) { + stats->value->u.boolean = *stats_data; + stats->value->type = QTYPE_QBOOL; + } else if (pdesc->size == 1) { + stats->value->u.scalar = *stats_data; + stats->value->type = QTYPE_QNUM; + } else { + int i; + for (i = 0; i < pdesc->size; i++) { + QAPI_LIST_PREPEND(val_list, stats_data[i]); + } + stats->value->u.list = val_list; + stats->value->type = QTYPE_QLIST; + } + + QAPI_LIST_PREPEND(stats_list, stats); + return stats_list; +} + +static StatsSchemaValueList *add_kvmschema_entry(struct kvm_stats_desc *pdesc, + StatsSchemaValueList *list, + Error **errp) +{ + StatsSchemaValueList *schema_entry = g_new0(StatsSchemaValueList, 1); + schema_entry->value = g_new0(StatsSchemaValue, 1); + + switch (pdesc->flags & KVM_STATS_TYPE_MASK) { + case KVM_STATS_TYPE_CUMULATIVE: + schema_entry->value->type = STATS_TYPE_CUMULATIVE; + break; + case KVM_STATS_TYPE_INSTANT: + schema_entry->value->type = STATS_TYPE_INSTANT; + break; + case KVM_STATS_TYPE_PEAK: + schema_entry->value->type = STATS_TYPE_PEAK; + break; + case KVM_STATS_TYPE_LINEAR_HIST: + schema_entry->value->type = STATS_TYPE_LINEAR_HISTOGRAM; + schema_entry->value->bucket_size = pdesc->bucket_size; + schema_entry->value->has_bucket_size = true; + break; + case KVM_STATS_TYPE_LOG_HIST: + schema_entry->value->type = STATS_TYPE_LOG2_HISTOGRAM; + break; + default: + goto exit; + } + + switch (pdesc->flags & KVM_STATS_UNIT_MASK) { + case KVM_STATS_UNIT_NONE: + break; + case KVM_STATS_UNIT_BOOLEAN: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_BOOLEAN; + break; + case KVM_STATS_UNIT_BYTES: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_BYTES; + break; + case KVM_STATS_UNIT_CYCLES: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_CYCLES; + break; + case KVM_STATS_UNIT_SECONDS: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_SECONDS; + break; + default: + goto exit; + } + + schema_entry->value->exponent = pdesc->exponent; + if (pdesc->exponent) { + switch (pdesc->flags & KVM_STATS_BASE_MASK) { + case KVM_STATS_BASE_POW10: + schema_entry->value->has_base = true; + schema_entry->value->base = 10; + break; + case KVM_STATS_BASE_POW2: + schema_entry->value->has_base = true; + schema_entry->value->base = 2; + break; + default: + goto exit; + } + } + + schema_entry->value->name = g_strdup(pdesc->name); + schema_entry->next = list; + return schema_entry; +exit: + g_free(schema_entry->value); + g_free(schema_entry); + return list; +} + +/* Cached stats descriptors */ +typedef struct StatsDescriptors { + const char *ident; /* cache key, currently the StatsTarget */ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + QTAILQ_ENTRY(StatsDescriptors) next; +} StatsDescriptors; + +static QTAILQ_HEAD(, StatsDescriptors) stats_descriptors = + QTAILQ_HEAD_INITIALIZER(stats_descriptors); + +/* + * Return the descriptors for 'target', that either have already been read + * or are retrieved from 'stats_fd'. + */ +static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd, + Error **errp) +{ + StatsDescriptors *descriptors; + const char *ident; + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + size_t size_desc; + ssize_t ret; + + ident = StatsTarget_str(target); + QTAILQ_FOREACH(descriptors, &stats_descriptors, next) { + if (g_str_equal(descriptors->ident, ident)) { + return descriptors; + } + } + + descriptors = g_new0(StatsDescriptors, 1); + + /* Read stats header */ + kvm_stats_header = g_malloc(sizeof(*kvm_stats_header)); + ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header)); + if (ret != sizeof(*kvm_stats_header)) { + error_setg(errp, "KVM stats: failed to read stats header: " + "expected %zu actual %zu", + sizeof(*kvm_stats_header), ret); + g_free(descriptors); + return NULL; + } + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Read stats descriptors */ + kvm_stats_desc = g_malloc0_n(kvm_stats_header->num_desc, size_desc); + ret = pread(stats_fd, kvm_stats_desc, + size_desc * kvm_stats_header->num_desc, + kvm_stats_header->desc_offset); + + if (ret != size_desc * kvm_stats_header->num_desc) { + error_setg(errp, "KVM stats: failed to read stats descriptors: " + "expected %zu actual %zu", + size_desc * kvm_stats_header->num_desc, ret); + g_free(descriptors); + g_free(kvm_stats_desc); + return NULL; + } + descriptors->kvm_stats_header = kvm_stats_header; + descriptors->kvm_stats_desc = kvm_stats_desc; + descriptors->ident = ident; + QTAILQ_INSERT_TAIL(&stats_descriptors, descriptors, next); + return descriptors; +} + +static void query_stats(StatsResultList **result, StatsTarget target, + strList *names, int stats_fd, Error **errp) +{ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + StatsDescriptors *descriptors; + g_autofree uint64_t *stats_data = NULL; + struct kvm_stats_desc *pdesc; + StatsList *stats_list = NULL; + size_t size_desc, size_data = 0; + ssize_t ret; + int i; + + descriptors = find_stats_descriptors(target, stats_fd, errp); + if (!descriptors) { + return; + } + + kvm_stats_header = descriptors->kvm_stats_header; + kvm_stats_desc = descriptors->kvm_stats_desc; + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Tally the total data size; read schema data */ + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + pdesc = (void *)kvm_stats_desc + i * size_desc; + size_data += pdesc->size * sizeof(*stats_data); + } + + stats_data = g_malloc0(size_data); + ret = pread(stats_fd, stats_data, size_data, kvm_stats_header->data_offset); + + if (ret != size_data) { + error_setg(errp, "KVM stats: failed to read data: " + "expected %zu actual %zu", size_data, ret); + return; + } + + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + uint64_t *stats; + pdesc = (void *)kvm_stats_desc + i * size_desc; + + /* Add entry to the list */ + stats = (void *)stats_data + pdesc->offset; + if (!apply_str_list_filter(pdesc->name, names)) { + continue; + } + stats_list = add_kvmstat_entry(pdesc, stats, stats_list, errp); + } + + if (!stats_list) { + return; + } + + switch (target) { + case STATS_TARGET_VM: + add_stats_entry(result, STATS_PROVIDER_KVM, NULL, stats_list); + break; + case STATS_TARGET_VCPU: + add_stats_entry(result, STATS_PROVIDER_KVM, + current_cpu->parent_obj.canonical_path, + stats_list); + break; + default: + break; + } +} + +static void query_stats_schema(StatsSchemaList **result, StatsTarget target, + int stats_fd, Error **errp) +{ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + StatsDescriptors *descriptors; + struct kvm_stats_desc *pdesc; + StatsSchemaValueList *stats_list = NULL; + size_t size_desc; + int i; + + descriptors = find_stats_descriptors(target, stats_fd, errp); + if (!descriptors) { + return; + } + + kvm_stats_header = descriptors->kvm_stats_header; + kvm_stats_desc = descriptors->kvm_stats_desc; + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Tally the total data size; read schema data */ + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + pdesc = (void *)kvm_stats_desc + i * size_desc; + stats_list = add_kvmschema_entry(pdesc, stats_list, errp); + } + + add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list); +} + +static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data) +{ + StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr; + int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + Error *local_err = NULL; + + if (stats_fd == -1) { + error_setg_errno(&local_err, errno, "KVM stats: ioctl failed"); + error_propagate(kvm_stats_args->errp, local_err); + return; + } + query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU, + kvm_stats_args->names, stats_fd, kvm_stats_args->errp); + close(stats_fd); +} + +static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data) +{ + StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr; + int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + Error *local_err = NULL; + + if (stats_fd == -1) { + error_setg_errno(&local_err, errno, "KVM stats: ioctl failed"); + error_propagate(kvm_stats_args->errp, local_err); + return; + } + query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd, + kvm_stats_args->errp); + close(stats_fd); +} + +static void query_stats_cb(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp) +{ + KVMState *s = kvm_state; + CPUState *cpu; + int stats_fd; + + switch (target) { + case STATS_TARGET_VM: + { + stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL); + if (stats_fd == -1) { + error_setg_errno(errp, errno, "KVM stats: ioctl failed"); + return; + } + query_stats(result, target, names, stats_fd, errp); + close(stats_fd); + break; + } + case STATS_TARGET_VCPU: + { + StatsArgs stats_args; + stats_args.result.stats = result; + stats_args.names = names; + stats_args.errp = errp; + CPU_FOREACH(cpu) { + if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) { + continue; + } + run_on_cpu(cpu, query_stats_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args)); + } + break; + } + default: + break; + } +} + +void query_stats_schemas_cb(StatsSchemaList **result, Error **errp) +{ + StatsArgs stats_args; + KVMState *s = kvm_state; + int stats_fd; + + stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL); + if (stats_fd == -1) { + error_setg_errno(errp, errno, "KVM stats: ioctl failed"); + return; + } + query_stats_schema(result, STATS_TARGET_VM, stats_fd, errp); + close(stats_fd); + + stats_args.result.schema = result; + stats_args.errp = errp; + run_on_cpu(first_cpu, query_stats_schema_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args)); +} diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 3345882d85d..2ac5f9c0361 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -148,3 +148,8 @@ bool kvm_dirty_ring_enabled(void) { return false; } + +uint32_t kvm_dirty_ring_size(void) +{ + return 0; +} diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index ea4a0dd2fbc..6ce8a34228c 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -21,6 +21,13 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } +int probe_access_flags(CPUArchState *env, target_ulong addr, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, uintptr_t retaddr) +{ + g_assert_not_reached(); +} + void *probe_access(CPUArchState *env, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5d131394139..cabb04fb1ea 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -905,6 +905,8 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, target_ulong dst_block, int exit_n, target_ulong cs_base, uint32_t flags, int cflags); +extern __thread int libafl_valid_current_cpu; + //// --- End LibAFL code --- /* main execution loop */ @@ -917,6 +919,12 @@ int cpu_exec(CPUState *cpu) /* replay_interrupt may need current_cpu */ current_cpu = cpu; +//// --- Begin LibAFL code --- + + libafl_valid_current_cpu = 1; + +//// --- End LibAFL code --- + if (cpu_handle_halt(cpu)) { return EXCP_HALTED; } diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index f90f4312ea3..a46f3a654de 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2248,7 +2248,7 @@ store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, const size_t tlb_off = offsetof(CPUTLBEntry, addr_write); uintptr_t index, index2; CPUTLBEntry *entry, *entry2; - target_ulong page2, tlb_addr, tlb_addr2; + target_ulong page1, page2, tlb_addr, tlb_addr2; MemOpIdx oi; size_t size2; int i; @@ -2256,15 +2256,17 @@ store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, /* * Ensure the second page is in the TLB. Note that the first page * is already guaranteed to be filled, and that the second page - * cannot evict the first. + * cannot evict the first. An exception to this rule is PAGE_WRITE_INV + * handling: the first page could have evicted itself. */ + page1 = addr & TARGET_PAGE_MASK; page2 = (addr + size) & TARGET_PAGE_MASK; size2 = (addr + size) & ~TARGET_PAGE_MASK; index2 = tlb_index(env, mmu_idx, page2); entry2 = tlb_entry(env, mmu_idx, page2); tlb_addr2 = tlb_addr_write(entry2); - if (!tlb_hit_page(tlb_addr2, page2)) { + if (page1 != page2 && !tlb_hit_page(tlb_addr2, page2)) { if (!victim_tlb_hit(env, mmu_idx, index2, tlb_off, page2)) { tlb_fill(env_cpu(env), page2, size2, MMU_DATA_STORE, mmu_idx, retaddr); diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index d50239e0e28..39610148872 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -70,6 +70,8 @@ static void *mttcg_cpu_thread_fn(void *arg) assert(tcg_enabled()); g_assert(!icount_enabled()); + tcg_cpu_init_cflags(cpu, current_machine->smp.max_cpus > 1); + rcu_register_thread(); force_rcu.notifier.notify = mttcg_force_rcu; force_rcu.cpu = cpu; @@ -95,6 +97,16 @@ static void *mttcg_cpu_thread_fn(void *arg) r = tcg_cpus_exec(cpu); qemu_mutex_lock_iothread(); switch (r) { + +//// --- Begin LibAFL code --- + +#define EXCP_LIBAFL_BP 0xf4775747 + + case EXCP_LIBAFL_BP: + break; + +//// --- End LibAFL code --- + case EXCP_DEBUG: cpu_handle_guest_debug(cpu); break; @@ -139,9 +151,6 @@ void mttcg_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - 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); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 1a72149f0e4..d23542e685f 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -139,6 +139,111 @@ static void rr_force_rcu(Notifier *notify, void *data) rr_kick_next_cpu(); } +//// --- Begin LibAFL code --- + +#define EXCP_LIBAFL_BP 0xf4775747 + +void libafl_cpu_thread_fn(CPUState *cpu); + +static void default_libafl_start_vcpu(CPUState *cpu) { + libafl_cpu_thread_fn(cpu); +} + +void (*libafl_start_vcpu)(CPUState *cpu) = default_libafl_start_vcpu; + +void libafl_cpu_thread_fn(CPUState *cpu) +{ + rr_start_kick_timer(); + + while (1) { + qemu_mutex_unlock_iothread(); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); + + if (icount_enabled()) { + /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ + icount_account_warp_timer(); + /* + * Run the timers here. This is much more efficient than + * waking up the I/O thread and waiting for completion. + */ + icount_handle_deadline(); + } + + replay_mutex_unlock(); + + if (!cpu) { + cpu = first_cpu; + } + + while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { + + qatomic_mb_set(&rr_current_cpu, cpu); + current_cpu = cpu; + + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, + (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); + + if (cpu_can_run(cpu)) { + int r; + + qemu_mutex_unlock_iothread(); + if (icount_enabled()) { + icount_prepare_for_run(cpu); + } + r = tcg_cpus_exec(cpu); + if (icount_enabled()) { + icount_process_data(cpu); + } + qemu_mutex_lock_iothread(); + + // TODO(libafl) should we have the iothread lock on? + if (r == EXCP_LIBAFL_BP) { + rr_stop_kick_timer(); + return; + } + + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + break; + } else if (r == EXCP_ATOMIC) { + qemu_mutex_unlock_iothread(); + cpu_exec_step_atomic(cpu); + qemu_mutex_lock_iothread(); + break; + } + } else if (cpu->stop) { + if (cpu->unplug) { + cpu = CPU_NEXT(cpu); + } + break; + } + + cpu = CPU_NEXT(cpu); + } /* while (cpu && !cpu->exit_request).. */ + + /* Does not need qatomic_mb_set because a spurious wakeup is okay. */ + qatomic_set(&rr_current_cpu, NULL); + + if (cpu && cpu->exit_request) { + qatomic_mb_set(&cpu->exit_request, 0); + } + + if (icount_enabled() && all_cpu_threads_idle()) { + /* + * When all cpus are sleeping (e.g in WFI), to avoid a deadlock + * in the main_loop, wake it up in order to start the warp timer. + */ + qemu_notify_event(); + } + + rr_wait_io_event(); + rr_deal_with_unplugged_cpus(); + } +} + +//// --- End LibAFL code --- + /* * In the single-threaded case each vCPU is simulated in turn. If * there is more than a single vCPU we create a simple timer to kick @@ -152,7 +257,9 @@ static void *rr_cpu_thread_fn(void *arg) Notifier force_rcu; CPUState *cpu = arg; - assert(tcg_enabled()); + g_assert(tcg_enabled()); + tcg_cpu_init_cflags(cpu, false); + rcu_register_thread(); force_rcu.notify = rr_force_rcu; rcu_add_force_rcu_notifier(&force_rcu); @@ -177,13 +284,25 @@ static void *rr_cpu_thread_fn(void *arg) } } - rr_start_kick_timer(); + // rr_start_kick_timer(); cpu = first_cpu; /* process any pending work */ cpu->exit_request = 1; + //// --- Begin LibAFL code --- + + libafl_start_vcpu(cpu); + + rcu_remove_force_rcu_notifier(&force_rcu); + rcu_unregister_thread(); + return NULL; + + //// --- End LibAFL code --- + + // The following dead code is from the original QEMU codebase + while (1) { qemu_mutex_unlock_iothread(); replay_mutex_lock(); @@ -275,9 +394,6 @@ void rr_start_vcpu_thread(CPUState *cpu) static QemuCond *single_tcg_halt_cond; static QemuThread *single_tcg_cpu_thread; - g_assert(tcg_enabled()); - tcg_cpu_init_cflags(cpu, false); - if (!single_tcg_cpu_thread) { cpu->thread = g_new0(QemuThread, 1); cpu->halt_cond = g_new0(QemuCond, 1); diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 684dc5a1371..786d90c08f3 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -97,16 +97,17 @@ static void tcg_accel_ops_init(AccelOpsClass *ops) ops->create_vcpu_thread = mttcg_start_vcpu_thread; ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; ops->handle_interrupt = tcg_handle_interrupt; - } else if (icount_enabled()) { - ops->create_vcpu_thread = rr_start_vcpu_thread; - ops->kick_vcpu_thread = rr_kick_vcpu_thread; - ops->handle_interrupt = icount_handle_interrupt; - ops->get_virtual_clock = icount_get; - ops->get_elapsed_ticks = icount_get; } else { ops->create_vcpu_thread = rr_start_vcpu_thread; ops->kick_vcpu_thread = rr_kick_vcpu_thread; - ops->handle_interrupt = tcg_handle_interrupt; + + if (icount_enabled()) { + ops->handle_interrupt = icount_handle_interrupt; + ops->get_virtual_clock = icount_get; + ops->get_elapsed_ticks = icount_get; + } else { + ops->handle_interrupt = tcg_handle_interrupt; + } } } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 47952eecd77..2eb56760f9d 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -82,6 +82,14 @@ static bool check_tcg_memory_orders_compatible(void) static bool default_mttcg_enabled(void) { + +//// --- Begin LibAFL code --- + + // Only the RR ops works with libafl_qemu, so avoid MTTCG by default + return false; + +//// --- End LibAFL code --- + if (icount_enabled() || TCG_OVERSIZED_GUEST) { return false; } else { diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 762c4109699..11c934cf3fb 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -33,15 +33,73 @@ //// --- Begin LibAFL code --- +#ifndef CONFIG_USER_ONLY + +#include "sysemu/runstate.h" +#include "migration/snapshot.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" + +void libafl_save_qemu_snapshot(char *name); +void libafl_load_qemu_snapshot(char *name); + +static void save_snapshot_cb(void* opaque) +{ + char* name = (char*)opaque; + Error *err = NULL; + if(!save_snapshot(name, true, NULL, false, NULL, &err)) { + error_report_err(err); + error_report("Could not save snapshot"); + } +} + +void libafl_save_qemu_snapshot(char *name) +{ + aio_bh_schedule_oneshot_full(qemu_get_aio_context(), save_snapshot_cb, (void*)name, "save_snapshot"); +} + +static void load_snapshot_cb(void* opaque) +{ + char* name = (char*)opaque; + Error *err = NULL; + + int saved_vm_running = runstate_is_running(); + vm_stop(RUN_STATE_RESTORE_VM); + + bool loaded = load_snapshot(name, NULL, false, NULL, &err); + + if(!loaded) { + error_report_err(err); + error_report("Could not load snapshot"); + } + if (loaded && saved_vm_running) { + vm_start(); + } +} + +void libafl_load_qemu_snapshot(char *name) +{ + aio_bh_schedule_oneshot_full(qemu_get_aio_context(), load_snapshot_cb, (void*)name, "load_snapshot"); +} + +#endif + #define EXCP_LIBAFL_BP 0xf4775747 -void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env) +void libafl_qemu_trigger_breakpoint(CPUState* cpu); + +void libafl_qemu_trigger_breakpoint(CPUState* cpu) { - CPUState* cpu = env_cpu(env); cpu->exception_index = EXCP_LIBAFL_BP; cpu_loop_exit(cpu); } +void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env) +{ + libafl_qemu_trigger_breakpoint(env_cpu(env)); +} + //// --- End LibAFL code --- /* 32-bit helpers */ diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 9e5525efcf3..1a74cffaa8a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -79,6 +79,7 @@ void libafl_gen_read_N(TCGv addr, size_t size); void libafl_gen_write(TCGv addr, MemOp ot); void libafl_gen_write_N(TCGv addr, size_t size); void libafl_gen_cmp(target_ulong pc, TCGv op0, TCGv op1, MemOp ot); +void libafl_gen_backdoor(target_ulong pc); static TCGHelperInfo libafl_exec_edge_hook_info = { .func = NULL, .name = "libafl_exec_edge_hook", \ @@ -656,6 +657,37 @@ void libafl_gen_cmp(target_ulong pc, TCGv op0, TCGv op1, MemOp ot) } } +static TCGHelperInfo libafl_exec_backdoor_hook_info = { + .func = NULL, .name = "libafl_exec_backdoor_hook", \ + .flags = dh_callflag(void), \ + .typemask = dh_typemask(void, 0) | dh_typemask(tl, 1) | dh_typemask(i64, 2) +}; + +struct libafl_backdoor_hook { + void (*exec)(target_ulong pc, uint64_t data); + uint64_t data; + TCGHelperInfo helper_info; + struct libafl_backdoor_hook* next; +}; + +struct libafl_backdoor_hook* libafl_backdoor_hooks; + +void libafl_add_backdoor_hook(void (*exec)(target_ulong pc, uint64_t data), + uint64_t data); +void libafl_add_backdoor_hook(void (*exec)(uint64_t id, uint64_t data), + uint64_t data) +{ + struct libafl_backdoor_hook* hook = malloc(sizeof(struct libafl_backdoor_hook)); + hook->exec = exec; + hook->data = data; + hook->next = libafl_backdoor_hooks; + libafl_backdoor_hooks = hook; + + memcpy(&hook->helper_info, &libafl_exec_backdoor_hook_info, sizeof(TCGHelperInfo)); + hook->helper_info.func = exec; + libafl_helper_table_add(&hook->helper_info); +} + //// --- End LibAFL code --- /* #define DEBUG_TB_INVALIDATE */ @@ -3063,6 +3095,15 @@ int page_get_flags(target_ulong address) return p->flags; } +/* + * Allow the target to decide if PAGE_TARGET_[12] may be reset. + * By default, they are not kept. + */ +#ifndef PAGE_TARGET_STICKY +#define PAGE_TARGET_STICKY 0 +#endif +#define PAGE_STICKY (PAGE_ANON | PAGE_TARGET_STICKY) + /* Modify the flags of a page and invalidate the code if necessary. The flag PAGE_WRITE_ORG is positioned automatically depending on PAGE_WRITE. The mmap_lock should already be held. */ @@ -3106,8 +3147,8 @@ void page_set_flags(target_ulong start, target_ulong end, int flags) p->target_data = NULL; p->flags = flags; } else { - /* Using mprotect on a page does not change MAP_ANON. */ - p->flags = (p->flags & PAGE_ANON) | flags; + /* Using mprotect on a page does not change sticky bits. */ + p->flags = (p->flags & PAGE_STICKY) | flags; } } } diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 5cdad299be6..3e2ce30f3a7 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -43,6 +43,15 @@ extern struct libafl_hook* libafl_qemu_hooks; struct libafl_hook* libafl_search_hook(target_ulong addr); +struct libafl_backdoor_hook { + void (*exec)(target_ulong pc, uint64_t data); + uint64_t data; + TCGHelperInfo helper_info; + struct libafl_backdoor_hook* next; +}; + +extern struct libafl_backdoor_hook* libafl_backdoor_hooks; + //// --- End LibAFL code --- /* Pairs with tcg_clear_temp_count. @@ -146,6 +155,41 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, libafl_gen_cur_pc = db->pc_next; + // 0x0f, 0x3a, 0xf2, 0x44 + uint8_t backdoor = translator_ldub(cpu->env_ptr, db, db->pc_next); + if (backdoor == 0x0f) { + backdoor = translator_ldub(cpu->env_ptr, db, db->pc_next +1); + if (backdoor == 0x3a) { + backdoor = translator_ldub(cpu->env_ptr, db, db->pc_next +2); + if (backdoor == 0xf2) { + backdoor = translator_ldub(cpu->env_ptr, db, db->pc_next +3); + if (backdoor == 0x44) { + struct libafl_backdoor_hook* hk = libafl_backdoor_hooks; + while (hk) { + TCGv tmp0 = tcg_const_tl(db->pc_next); + TCGv_i64 tmp1 = tcg_const_i64(hk->data); +#if TARGET_LONG_BITS == 32 + TCGTemp *tmp2[2] = { tcgv_i32_temp(tmp0), tcgv_i64_temp(tmp1) }; +#else + TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1) }; +#endif + tcg_gen_callN(hk->exec, NULL, 2, tmp2); +#if TARGET_LONG_BITS == 32 + tcg_temp_free_i32(tmp0); +#else + tcg_temp_free_i64(tmp0); +#endif + tcg_temp_free_i64(tmp1); + hk = hk->next; + } + + db->pc_next += 4; + goto post_translate_insn; + } + } + } + } + //// --- End LibAFL code --- /* Disassemble one instruction. The translate_insn hook should @@ -162,6 +206,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, ops->translate_insn(db, cpu); } +post_translate_insn: + /* Stop translation if translate_insn so indicated. */ if (db->is_jmp != DISAS_NEXT) { break; diff --git a/audio/meson.build b/audio/meson.build index 94dab16891d..3abee908602 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -28,7 +28,7 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: [gio, pixman, opengl, 'CONFIG_GIO'], if_true: files('dbusaudio.c')) + module_ss.add(when: gio, if_true: files('dbusaudio.c')) audio_modules += {'dbus': module_ss} endif diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index 0671bf9f3e5..125cbad1d37 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" #include "crypto/cipher.h" +#include "crypto/akcipher.h" #include "qom/object.h" @@ -42,10 +43,11 @@ typedef struct CryptoDevBackendBuiltinSession { QCryptoCipher *cipher; uint8_t direction; /* encryption or decryption */ uint8_t type; /* cipher? hash? aead? */ + QCryptoAkCipher *akcipher; QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next; } CryptoDevBackendBuiltinSession; -/* Max number of symmetric sessions */ +/* Max number of symmetric/asymmetric sessions */ #define MAX_NUM_SESSIONS 256 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512 @@ -80,15 +82,17 @@ static void cryptodev_builtin_init( backend->conf.crypto_services = 1u << VIRTIO_CRYPTO_SERVICE_CIPHER | 1u << VIRTIO_CRYPTO_SERVICE_HASH | - 1u << VIRTIO_CRYPTO_SERVICE_MAC; + 1u << VIRTIO_CRYPTO_SERVICE_MAC | + 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER; backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; + backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; /* * Set the Maximum length of crypto request. * Why this value? Just avoid to overflow when * memory allocation for each crypto request. */ - backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo); + backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo); backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN; backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN; @@ -148,6 +152,55 @@ cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp) return -1; } +static int cryptodev_builtin_get_rsa_hash_algo( + int virtio_rsa_hash, Error **errp) +{ + switch (virtio_rsa_hash) { + case VIRTIO_CRYPTO_RSA_MD5: + return QCRYPTO_HASH_ALG_MD5; + + case VIRTIO_CRYPTO_RSA_SHA1: + return QCRYPTO_HASH_ALG_SHA1; + + case VIRTIO_CRYPTO_RSA_SHA256: + return QCRYPTO_HASH_ALG_SHA256; + + case VIRTIO_CRYPTO_RSA_SHA512: + return QCRYPTO_HASH_ALG_SHA512; + + default: + error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash); + return -1; + } +} + +static int cryptodev_builtin_set_rsa_options( + int virtio_padding_algo, + int virtio_hash_algo, + QCryptoAkCipherOptionsRSA *opt, + Error **errp) +{ + if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) { + int hash_alg; + + hash_alg = cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp); + if (hash_alg < 0) { + return -1; + } + opt->hash_alg = hash_alg; + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1; + return 0; + } + + if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) { + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + return 0; + } + + error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo); + return -1; +} + static int cryptodev_builtin_create_cipher_session( CryptoDevBackendBuiltin *builtin, CryptoDevBackendSymSessionInfo *sess_info, @@ -240,26 +293,89 @@ static int cryptodev_builtin_create_cipher_session( return index; } -static int64_t cryptodev_builtin_sym_create_session( +static int cryptodev_builtin_create_akcipher_session( + CryptoDevBackendBuiltin *builtin, + CryptoDevBackendAsymSessionInfo *sess_info, + Error **errp) +{ + CryptoDevBackendBuiltinSession *sess; + QCryptoAkCipher *akcipher; + int index; + QCryptoAkCipherKeyType type; + QCryptoAkCipherOptions opts; + + switch (sess_info->algo) { + case VIRTIO_CRYPTO_AKCIPHER_RSA: + opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; + if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo, + sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) { + return -1; + } + break; + + /* TODO support DSA&ECDSA until qemu crypto framework support these */ + + default: + error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo); + return -1; + } + + switch (sess_info->keytype) { + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC; + break; + + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE; + break; + + default: + error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype); + return -1; + } + + index = cryptodev_builtin_get_unused_session_index(builtin); + if (index < 0) { + error_setg(errp, "Total number of sessions created exceeds %u", + MAX_NUM_SESSIONS); + return -1; + } + + akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key, + sess_info->keylen, errp); + if (!akcipher) { + return -1; + } + + sess = g_new0(CryptoDevBackendBuiltinSession, 1); + sess->akcipher = akcipher; + + builtin->sessions[index] = sess; + + return index; +} + +static int64_t cryptodev_builtin_create_session( CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, + CryptoDevBackendSessionInfo *sess_info, uint32_t queue_index, Error **errp) { CryptoDevBackendBuiltin *builtin = CRYPTODEV_BACKEND_BUILTIN(backend); - int64_t session_id = -1; - int ret; + CryptoDevBackendSymSessionInfo *sym_sess_info; + CryptoDevBackendAsymSessionInfo *asym_sess_info; switch (sess_info->op_code) { case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: - ret = cryptodev_builtin_create_cipher_session( - builtin, sess_info, errp); - if (ret < 0) { - return ret; - } else { - session_id = ret; - } - break; + sym_sess_info = &sess_info->u.sym_sess_info; + return cryptodev_builtin_create_cipher_session( + builtin, sym_sess_info, errp); + + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + asym_sess_info = &sess_info->u.asym_sess_info; + return cryptodev_builtin_create_akcipher_session( + builtin, asym_sess_info, errp); + case VIRTIO_CRYPTO_HASH_CREATE_SESSION: case VIRTIO_CRYPTO_MAC_CREATE_SESSION: default: @@ -268,50 +384,44 @@ static int64_t cryptodev_builtin_sym_create_session( return -1; } - return session_id; + return -1; } -static int cryptodev_builtin_sym_close_session( +static int cryptodev_builtin_close_session( CryptoDevBackend *backend, uint64_t session_id, uint32_t queue_index, Error **errp) { CryptoDevBackendBuiltin *builtin = CRYPTODEV_BACKEND_BUILTIN(backend); + CryptoDevBackendBuiltinSession *session; assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]); - qcrypto_cipher_free(builtin->sessions[session_id]->cipher); - g_free(builtin->sessions[session_id]); + session = builtin->sessions[session_id]; + if (session->cipher) { + qcrypto_cipher_free(session->cipher); + } else if (session->akcipher) { + qcrypto_akcipher_free(session->akcipher); + } + + g_free(session); builtin->sessions[session_id] = NULL; return 0; } static int cryptodev_builtin_sym_operation( - CryptoDevBackend *backend, - CryptoDevBackendSymOpInfo *op_info, - uint32_t queue_index, Error **errp) + CryptoDevBackendBuiltinSession *sess, + CryptoDevBackendSymOpInfo *op_info, Error **errp) { - CryptoDevBackendBuiltin *builtin = - CRYPTODEV_BACKEND_BUILTIN(backend); - CryptoDevBackendBuiltinSession *sess; int ret; - if (op_info->session_id >= MAX_NUM_SESSIONS || - builtin->sessions[op_info->session_id] == NULL) { - error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", - op_info->session_id); - return -VIRTIO_CRYPTO_INVSESS; - } - if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { error_setg(errp, "Algorithm chain is unsupported for cryptdoev-builtin"); return -VIRTIO_CRYPTO_NOTSUPP; } - sess = builtin->sessions[op_info->session_id]; - if (op_info->iv_len > 0) { ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, op_info->iv_len, errp); @@ -333,9 +443,99 @@ static int cryptodev_builtin_sym_operation( return -VIRTIO_CRYPTO_ERR; } } + + return VIRTIO_CRYPTO_OK; +} + +static int cryptodev_builtin_asym_operation( + CryptoDevBackendBuiltinSession *sess, uint32_t op_code, + CryptoDevBackendAsymOpInfo *op_info, Error **errp) +{ + int ret; + + switch (op_code) { + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: + ret = qcrypto_akcipher_encrypt(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: + ret = qcrypto_akcipher_decrypt(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + case VIRTIO_CRYPTO_AKCIPHER_SIGN: + ret = qcrypto_akcipher_sign(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + case VIRTIO_CRYPTO_AKCIPHER_VERIFY: + ret = qcrypto_akcipher_verify(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + default: + return -VIRTIO_CRYPTO_ERR; + } + + if (ret < 0) { + if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) { + return -VIRTIO_CRYPTO_KEY_REJECTED; + } + return -VIRTIO_CRYPTO_ERR; + } + + /* Buffer is too short, typically the driver should handle this case */ + if (unlikely(ret > op_info->dst_len)) { + if (errp && !*errp) { + error_setg(errp, "dst buffer too short"); + } + + return -VIRTIO_CRYPTO_ERR; + } + + op_info->dst_len = ret; + return VIRTIO_CRYPTO_OK; } +static int cryptodev_builtin_operation( + CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info, + uint32_t queue_index, Error **errp) +{ + CryptoDevBackendBuiltin *builtin = + CRYPTODEV_BACKEND_BUILTIN(backend); + CryptoDevBackendBuiltinSession *sess; + CryptoDevBackendSymOpInfo *sym_op_info; + CryptoDevBackendAsymOpInfo *asym_op_info; + enum CryptoDevBackendAlgType algtype = op_info->algtype; + int ret = -VIRTIO_CRYPTO_ERR; + + if (op_info->session_id >= MAX_NUM_SESSIONS || + builtin->sessions[op_info->session_id] == NULL) { + error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", + op_info->session_id); + return -VIRTIO_CRYPTO_INVSESS; + } + + sess = builtin->sessions[op_info->session_id]; + if (algtype == CRYPTODEV_BACKEND_ALG_SYM) { + sym_op_info = op_info->u.sym_op_info; + ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp); + } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) { + asym_op_info = op_info->u.asym_op_info; + ret = cryptodev_builtin_asym_operation(sess, op_info->op_code, + asym_op_info, errp); + } + + return ret; +} + static void cryptodev_builtin_cleanup( CryptoDevBackend *backend, Error **errp) @@ -348,7 +548,7 @@ static void cryptodev_builtin_cleanup( for (i = 0; i < MAX_NUM_SESSIONS; i++) { if (builtin->sessions[i] != NULL) { - cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort); + cryptodev_builtin_close_session(backend, i, 0, &error_abort); } } @@ -370,9 +570,9 @@ cryptodev_builtin_class_init(ObjectClass *oc, void *data) bc->init = cryptodev_builtin_init; bc->cleanup = cryptodev_builtin_cleanup; - bc->create_session = cryptodev_builtin_sym_create_session; - bc->close_session = cryptodev_builtin_sym_close_session; - bc->do_sym_op = cryptodev_builtin_sym_operation; + bc->create_session = cryptodev_builtin_create_session; + bc->close_session = cryptodev_builtin_close_session; + bc->do_op = cryptodev_builtin_operation; } static const TypeInfo cryptodev_builtin_info = { diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index bedb4524742..5443a59153a 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -259,7 +259,33 @@ static int64_t cryptodev_vhost_user_sym_create_session( return -1; } -static int cryptodev_vhost_user_sym_close_session( +static int64_t cryptodev_vhost_user_create_session( + CryptoDevBackend *backend, + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, Error **errp) +{ + uint32_t op_code = sess_info->op_code; + CryptoDevBackendSymSessionInfo *sym_sess_info; + + switch (op_code) { + case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: + case VIRTIO_CRYPTO_HASH_CREATE_SESSION: + case VIRTIO_CRYPTO_MAC_CREATE_SESSION: + case VIRTIO_CRYPTO_AEAD_CREATE_SESSION: + sym_sess_info = &sess_info->u.sym_sess_info; + return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info, + queue_index, errp); + default: + error_setg(errp, "Unsupported opcode :%" PRIu32 "", + sess_info->op_code); + return -1; + + } + + return -1; +} + +static int cryptodev_vhost_user_close_session( CryptoDevBackend *backend, uint64_t session_id, uint32_t queue_index, Error **errp) @@ -351,9 +377,9 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, void *data) bc->init = cryptodev_vhost_user_init; bc->cleanup = cryptodev_vhost_user_cleanup; - bc->create_session = cryptodev_vhost_user_sym_create_session; - bc->close_session = cryptodev_vhost_user_sym_close_session; - bc->do_sym_op = NULL; + bc->create_session = cryptodev_vhost_user_create_session; + bc->close_session = cryptodev_vhost_user_close_session; + bc->do_op = NULL; object_class_property_add_str(oc, "chardev", cryptodev_vhost_user_get_chardev, diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 2b105e433c5..33eb4e1a705 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -72,9 +72,9 @@ void cryptodev_backend_cleanup( } } -int64_t cryptodev_backend_sym_create_session( +int64_t cryptodev_backend_create_session( CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, + CryptoDevBackendSessionInfo *sess_info, uint32_t queue_index, Error **errp) { CryptoDevBackendClass *bc = @@ -87,7 +87,7 @@ int64_t cryptodev_backend_sym_create_session( return -1; } -int cryptodev_backend_sym_close_session( +int cryptodev_backend_close_session( CryptoDevBackend *backend, uint64_t session_id, uint32_t queue_index, Error **errp) @@ -102,16 +102,16 @@ int cryptodev_backend_sym_close_session( return -1; } -static int cryptodev_backend_sym_operation( +static int cryptodev_backend_operation( CryptoDevBackend *backend, - CryptoDevBackendSymOpInfo *op_info, + CryptoDevBackendOpInfo *op_info, uint32_t queue_index, Error **errp) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(backend); - if (bc->do_sym_op) { - return bc->do_sym_op(backend, op_info, queue_index, errp); + if (bc->do_op) { + return bc->do_op(backend, op_info, queue_index, errp); } return -VIRTIO_CRYPTO_ERR; @@ -123,20 +123,18 @@ int cryptodev_backend_crypto_operation( uint32_t queue_index, Error **errp) { VirtIOCryptoReq *req = opaque; + CryptoDevBackendOpInfo *op_info = &req->op_info; + enum CryptoDevBackendAlgType algtype = req->flags; - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { - CryptoDevBackendSymOpInfo *op_info; - op_info = req->u.sym_op_info; - - return cryptodev_backend_sym_operation(backend, - op_info, queue_index, errp); - } else { + if ((algtype != CRYPTODEV_BACKEND_ALG_SYM) + && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) { error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "", - req->flags); - return -VIRTIO_CRYPTO_NOTSUPP; + algtype); + + return -VIRTIO_CRYPTO_NOTSUPP; } - return -VIRTIO_CRYPTO_ERR; + return cryptodev_backend_operation(backend, op_info, queue_index, errp); } static void diff --git a/block.c b/block.c index 2c00dddd809..bc85f46eed6 100644 --- a/block.c +++ b/block.c @@ -1037,7 +1037,7 @@ static int find_image_format(BlockBackend *file, const char *filename, return ret; } - ret = blk_pread(file, 0, buf, sizeof(buf)); + ret = blk_pread(file, 0, sizeof(buf), buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read image for determining its " "format"); @@ -1045,14 +1045,16 @@ static int find_image_format(BlockBackend *file, const char *filename, return ret; } - drv = bdrv_probe_all(buf, ret, filename); + drv = bdrv_probe_all(buf, sizeof(buf), filename); if (!drv) { error_setg(errp, "Could not determine image format: No compatible " "driver found"); - ret = -ENOENT; + *pdrv = NULL; + return -ENOENT; } + *pdrv = drv; - return ret; + return 0; } /** diff --git a/block/backup.c b/block/backup.c index 5cfd0b999ce..b2b649e3055 100644 --- a/block/backup.c +++ b/block/backup.c @@ -228,15 +228,13 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) static void backup_init_bcs_bitmap(BackupBlockJob *job) { - bool ret; uint64_t estimate; BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs); if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) { bdrv_clear_dirty_bitmap(bcs_bitmap, NULL); - ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, - NULL, true); - assert(ret); + bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, NULL, + true); } else if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { /* * We can't hog the coroutine to initialize this thoroughly. diff --git a/block/blklogwrites.c b/block/blklogwrites.c index f7a251e91f9..e3c6c4039c7 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -107,8 +107,8 @@ static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, struct log_write_entry cur_entry; while (cur_idx < nr_entries) { - int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry, - sizeof(cur_entry)); + int read_ret = bdrv_pread(log, cur_sector << sector_bits, + sizeof(cur_entry), &cur_entry, 0); if (read_ret < 0) { error_setg_errno(errp, -read_ret, "Failed to read log entry %"PRIu64, cur_idx); @@ -190,7 +190,7 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, log_sb.nr_entries = cpu_to_le64(0); log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE); } else { - ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb)); + ret = bdrv_pread(s->log_file, 0, sizeof(log_sb), &log_sb, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read log superblock"); goto fail_log; diff --git a/block/block-backend.c b/block/block-backend.c index e0e1aff4b1b..d4a5df2ac2c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -56,9 +56,6 @@ struct BlockBackend { const BlockDevOps *dev_ops; void *dev_opaque; - /* the block size for which the guest device expects atomicity */ - int guest_block_size; - /* If the BDS tree is removed, some of its options are stored here (which * can be used to restore those options in the new BDS on insert) */ BlockBackendRootState root_state; @@ -998,7 +995,6 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) blk->dev = NULL; blk->dev_ops = NULL; blk->dev_opaque = NULL; - blk->guest_block_size = 512; blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); blk_unref(blk); } @@ -1062,7 +1058,7 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, blk->dev_opaque = opaque; /* Are we currently quiesced? Should we enforce this right now? */ - if (blk->quiesce_counter && ops->drained_begin) { + if (blk->quiesce_counter && ops && ops->drained_begin) { ops->drained_begin(opaque); } } @@ -1284,9 +1280,10 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn -blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn +blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { int ret; BlockDriverState *bs; @@ -1311,11 +1308,23 @@ blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, bytes, false); } - ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); + ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, + flags); bdrv_dec_in_flight(bs); return ret; } +int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, + void *buf, BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); + + assert(bytes <= SIZE_MAX); + + return blk_co_preadv(blk, offset, bytes, &qiov, flags); +} + int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) @@ -1324,14 +1333,28 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, IO_OR_GS_CODE(); blk_inc_in_flight(blk); - ret = blk_co_do_preadv(blk, offset, bytes, qiov, flags); + ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, 0, flags); + blk_dec_in_flight(blk); + + return ret; +} + +int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, BdrvRequestFlags flags) +{ + int ret; + IO_OR_GS_CODE(); + + blk_inc_in_flight(blk); + ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, qiov_offset, flags); blk_dec_in_flight(blk); return ret; } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn +static int coroutine_fn blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) @@ -1383,6 +1406,17 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, return ret; } +int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, + const void *buf, BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); + + assert(bytes <= SIZE_MAX); + + return blk_co_pwritev(blk, offset, bytes, &qiov, flags); +} + int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) @@ -1391,20 +1425,6 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); } -static int coroutine_fn blk_pwritev_part(BlockBackend *blk, int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags) -{ - int ret; - - blk_inc_in_flight(blk); - ret = blk_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); - blk_dec_in_flight(blk); - - return ret; -} - typedef struct BlkRwCo { BlockBackend *blk; int64_t offset; @@ -1413,14 +1433,6 @@ typedef struct BlkRwCo { BdrvRequestFlags flags; } BlkRwCo; -int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) -{ - IO_OR_GS_CODE(); - return blk_pwritev_part(blk, offset, bytes, NULL, 0, - flags | BDRV_REQ_ZERO_WRITE); -} - int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) { GLOBAL_STATE_CODE(); @@ -1541,8 +1553,8 @@ static void blk_aio_read_entry(void *opaque) QEMUIOVector *qiov = rwco->iobuf; assert(qiov->size == acb->bytes); - rwco->ret = blk_co_do_preadv(rwco->blk, rwco->offset, acb->bytes, - qiov, rwco->flags); + rwco->ret = blk_co_do_preadv_part(rwco->blk, rwco->offset, acb->bytes, qiov, + 0, rwco->flags); blk_aio_complete(acb); } @@ -1567,31 +1579,6 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } -int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); - - blk_inc_in_flight(blk); - ret = blk_do_preadv(blk, offset, bytes, &qiov, 0); - blk_dec_in_flight(blk); - - return ret < 0 ? ret : bytes; -} - -int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, - BdrvRequestFlags flags) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); - - ret = blk_pwritev_part(blk, offset, bytes, &qiov, 0, flags); - - return ret < 0 ? ret : bytes; -} - int64_t blk_getlength(BlockBackend *blk) { IO_CODE(); @@ -1655,7 +1642,7 @@ void blk_aio_cancel_async(BlockAIOCB *acb) } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn +static int coroutine_fn blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { IO_CODE(); @@ -1669,13 +1656,14 @@ blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) return bdrv_co_ioctl(blk_bs(blk), req, buf); } -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, + void *buf) { int ret; IO_OR_GS_CODE(); blk_inc_in_flight(blk); - ret = blk_do_ioctl(blk, req, buf); + ret = blk_co_do_ioctl(blk, req, buf); blk_dec_in_flight(blk); return ret; @@ -1699,7 +1687,7 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn +static int coroutine_fn blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; @@ -1746,20 +1734,8 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, return ret; } -int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) -{ - int ret; - IO_OR_GS_CODE(); - - blk_inc_in_flight(blk); - ret = blk_do_pdiscard(blk, offset, bytes); - blk_dec_in_flight(blk); - - return ret; -} - /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn blk_co_do_flush(BlockBackend *blk) +static int coroutine_fn blk_co_do_flush(BlockBackend *blk) { blk_wait_while_drained(blk); IO_CODE(); @@ -1799,17 +1775,6 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) return ret; } -int blk_flush(BlockBackend *blk) -{ - int ret; - - blk_inc_in_flight(blk); - ret = blk_do_flush(blk); - blk_dec_in_flight(blk); - - return ret; -} - void blk_drain(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); @@ -2100,12 +2065,6 @@ int blk_get_max_iov(BlockBackend *blk) return blk->root->bs->bl.max_iov; } -void blk_set_guest_block_size(BlockBackend *blk, int align) -{ - IO_CODE(); - blk->guest_block_size = align; -} - void *blk_try_blockalign(BlockBackend *blk, size_t size) { IO_CODE(); @@ -2347,17 +2306,18 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE); } -int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int64_t bytes) +int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, + int64_t bytes, const void *buf) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); IO_OR_GS_CODE(); - return blk_pwritev_part(blk, offset, bytes, &qiov, 0, - BDRV_REQ_WRITE_COMPRESSED); + return blk_co_pwritev_part(blk, offset, bytes, &qiov, 0, + BDRV_REQ_WRITE_COMPRESSED); } -int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) +int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp) { IO_OR_GS_CODE(); if (!blk_is_available(blk)) { @@ -2365,7 +2325,7 @@ int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, return -ENOMEDIUM; } - return bdrv_truncate(blk->root, offset, exact, prealloc, flags, errp); + return bdrv_co_truncate(blk->root, offset, exact, prealloc, flags, errp); } int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, diff --git a/block/block-copy.c b/block/block-copy.c index ec46775ea5c..bb947afddad 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -883,23 +883,42 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) return ret; } +static void coroutine_fn block_copy_async_co_entry(void *opaque) +{ + block_copy_common(opaque); +} + int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes, - bool ignore_ratelimit) + bool ignore_ratelimit, uint64_t timeout_ns, + BlockCopyAsyncCallbackFunc cb, + void *cb_opaque) { - BlockCopyCallState call_state = { + int ret; + BlockCopyCallState *call_state = g_new(BlockCopyCallState, 1); + + *call_state = (BlockCopyCallState) { .s = s, .offset = start, .bytes = bytes, .ignore_ratelimit = ignore_ratelimit, .max_workers = BLOCK_COPY_MAX_WORKERS, + .cb = cb, + .cb_opaque = cb_opaque, }; - return block_copy_common(&call_state); -} + ret = qemu_co_timeout(block_copy_async_co_entry, call_state, timeout_ns, + g_free); + if (ret < 0) { + assert(ret == -ETIMEDOUT); + block_copy_call_cancel(call_state); + /* call_state will be freed by running coroutine. */ + return ret; + } -static void coroutine_fn block_copy_async_co_entry(void *opaque) -{ - block_copy_common(opaque); + ret = call_state->ret; + g_free(call_state); + + return ret; } BlockCopyCallState *block_copy_async(BlockCopyState *s, diff --git a/block/bochs.c b/block/bochs.c index 4d68658087b..b76f34fe030 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -116,7 +116,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + ret = bdrv_pread(bs->file, 0, sizeof(bochs), &bochs, 0); if (ret < 0) { return ret; } @@ -150,8 +150,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, - s->catalog_size * 4); + ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_size * 4, + s->catalog_bitmap, 0); if (ret < 0) { goto fail; } @@ -224,8 +224,8 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) (s->extent_blocks + s->bitmap_blocks)); /* read in bitmap for current extent */ - ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), - &bitmap_entry, 1); + ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), 1, + &bitmap_entry, 0); if (ret < 0) { return ret; } diff --git a/block/cloop.c b/block/cloop.c index b8c6d0eccdb..40b146e7148 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -78,7 +78,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, } /* read header */ - ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + ret = bdrv_pread(bs->file, 128, 4, &s->block_size, 0); if (ret < 0) { return ret; } @@ -104,7 +104,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); + ret = bdrv_pread(bs->file, 128 + 4, 4, &s->n_blocks, 0); if (ret < 0) { return ret; } @@ -135,7 +135,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); + ret = bdrv_pread(bs->file, 128 + 4 + 4, offsets_size, s->offsets, 0); if (ret < 0) { goto fail; } @@ -220,9 +220,9 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) int ret; uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num]; - ret = bdrv_pread(bs->file, s->offsets[block_num], - s->compressed_block, bytes); - if (ret != bytes) { + ret = bdrv_pread(bs->file, s->offsets[block_num], bytes, + s->compressed_block, 0); + if (ret < 0) { return -1; } diff --git a/block/commit.c b/block/commit.c index 851d1c557a1..38571510cbd 100644 --- a/block/commit.c +++ b/block/commit.c @@ -527,12 +527,12 @@ int bdrv_commit(BlockDriverState *bs) goto ro_cleanup; } if (ret) { - ret = blk_pread(src, offset, buf, n); + ret = blk_pread(src, offset, n, buf, 0); if (ret < 0) { goto ro_cleanup; } - ret = blk_pwrite(backing, offset, buf, n, 0); + ret = blk_pwrite(backing, offset, n, buf, 0); if (ret < 0) { goto ro_cleanup; } diff --git a/block/copy-before-write.c b/block/copy-before-write.c index a8a06fdc09b..c24b8dd1177 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qapi/qmp/qjson.h" #include "sysemu/block-backend.h" #include "qemu/cutils.h" @@ -40,6 +41,8 @@ typedef struct BDRVCopyBeforeWriteState { BlockCopyState *bcs; BdrvChild *target; + OnCbwError on_cbw_error; + uint32_t cbw_timeout_ns; /* * @lock: protects access to @access_bitmap, @done_bitmap and @@ -64,6 +67,14 @@ typedef struct BDRVCopyBeforeWriteState { * node. These areas must not be rewritten by guest. */ BlockReqList frozen_read_reqs; + + /* + * @snapshot_error is normally zero. But on first copy-before-write failure + * when @on_cbw_error == ON_CBW_ERROR_BREAK_SNAPSHOT, @snapshot_error takes + * value of this error (<0). After that all in-flight and further + * snapshot-API requests will fail with that error. + */ + int snapshot_error; } BDRVCopyBeforeWriteState; static coroutine_fn int cbw_co_preadv( @@ -73,6 +84,13 @@ static coroutine_fn int cbw_co_preadv( return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } +static void block_copy_cb(void *opaque) +{ + BlockDriverState *bs = opaque; + + bdrv_dec_in_flight(bs); +} + /* * Do copy-before-write operation. * @@ -94,16 +112,36 @@ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, return 0; } + if (s->snapshot_error) { + return 0; + } + off = QEMU_ALIGN_DOWN(offset, cluster_size); end = QEMU_ALIGN_UP(offset + bytes, cluster_size); - ret = block_copy(s->bcs, off, end - off, true); - if (ret < 0) { + /* + * Increase in_flight, so that in case of timed-out block-copy, the + * remaining background block_copy() request (which can't be immediately + * cancelled by timeout) is presented in bs->in_flight. This way we are + * sure that on bs close() we'll previously wait for all timed-out but yet + * running block_copy calls. + */ + bdrv_inc_in_flight(bs); + ret = block_copy(s->bcs, off, end - off, true, s->cbw_timeout_ns, + block_copy_cb, bs); + if (ret < 0 && s->on_cbw_error == ON_CBW_ERROR_BREAK_GUEST_WRITE) { return ret; } WITH_QEMU_LOCK_GUARD(&s->lock) { - bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off); + if (ret < 0) { + assert(s->on_cbw_error == ON_CBW_ERROR_BREAK_SNAPSHOT); + if (!s->snapshot_error) { + s->snapshot_error = ret; + } + } else { + bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off); + } reqlist_wait_all(&s->frozen_read_reqs, off, end - off, &s->lock); } @@ -175,6 +213,11 @@ static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs, QEMU_LOCK_GUARD(&s->lock); + if (s->snapshot_error) { + g_free(req); + return NULL; + } + if (bdrv_dirty_bitmap_next_zero(s->access_bitmap, offset, bytes) != -1) { g_free(req); return NULL; @@ -328,46 +371,36 @@ static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, } } -static bool cbw_parse_bitmap_option(QDict *options, BdrvDirtyBitmap **bitmap, - Error **errp) +static BlockdevOptions *cbw_parse_options(QDict *options, Error **errp) { - QDict *bitmap_qdict = NULL; - BlockDirtyBitmap *bmp_param = NULL; + BlockdevOptions *opts = NULL; Visitor *v = NULL; - bool ret = false; - *bitmap = NULL; + qdict_put_str(options, "driver", "copy-before-write"); - qdict_extract_subqdict(options, &bitmap_qdict, "bitmap."); - if (!qdict_size(bitmap_qdict)) { - ret = true; - goto out; - } - - v = qobject_input_visitor_new_flat_confused(bitmap_qdict, errp); + v = qobject_input_visitor_new_flat_confused(options, errp); if (!v) { goto out; } - visit_type_BlockDirtyBitmap(v, NULL, &bmp_param, errp); - if (!bmp_param) { - goto out; - } - - *bitmap = block_dirty_bitmap_lookup(bmp_param->node, bmp_param->name, NULL, - errp); - if (!*bitmap) { + visit_type_BlockdevOptions(v, NULL, &opts, errp); + if (!opts) { goto out; } - ret = true; + /* + * Delete options which we are going to parse through BlockdevOptions + * object for original options. + */ + qdict_extract_subqdict(options, NULL, "bitmap"); + qdict_del(options, "on-cbw-error"); + qdict_del(options, "cbw-timeout"); out: - qapi_free_BlockDirtyBitmap(bmp_param); visit_free(v); - qobject_unref(bitmap_qdict); + qdict_del(options, "driver"); - return ret; + return opts; } static int cbw_open(BlockDriverState *bs, QDict *options, int flags, @@ -376,6 +409,15 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, BDRVCopyBeforeWriteState *s = bs->opaque; BdrvDirtyBitmap *bitmap = NULL; int64_t cluster_size; + g_autoptr(BlockdevOptions) full_opts = NULL; + BlockdevOptionsCbw *opts; + + full_opts = cbw_parse_options(options, errp); + if (!full_opts) { + return -EINVAL; + } + assert(full_opts->driver == BLOCKDEV_DRIVER_COPY_BEFORE_WRITE); + opts = &full_opts->u.copy_before_write; bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, @@ -390,9 +432,17 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - if (!cbw_parse_bitmap_option(options, &bitmap, errp)) { - return -EINVAL; + if (opts->has_bitmap) { + bitmap = block_dirty_bitmap_lookup(opts->bitmap->node, + opts->bitmap->name, NULL, errp); + if (!bitmap) { + return -EINVAL; + } } + s->on_cbw_error = opts->has_on_cbw_error ? opts->on_cbw_error : + ON_CBW_ERROR_BREAK_GUEST_WRITE; + s->cbw_timeout_ns = opts->has_cbw_timeout ? + opts->cbw_timeout * NANOSECONDS_PER_SECOND : 0; bs->total_sectors = bs->file->bs->total_sectors; bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | diff --git a/block/coroutines.h b/block/coroutines.h index 830ecaa733c..3a2bad564fe 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -63,25 +63,6 @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); -int coroutine_fn -blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - - -int coroutine_fn -blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); - -int coroutine_fn -blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); - -int coroutine_fn -blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); - -int coroutine_fn blk_co_do_flush(BlockBackend *blk); - - /* * "I/O or GS" API functions. These functions can run without * the BQL, but only in one specific iothread/main loop. @@ -90,14 +71,6 @@ int coroutine_fn blk_co_do_flush(BlockBackend *blk); * the "I/O or GS" API. */ -int generated_co_wrapper -bdrv_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - -int generated_co_wrapper -bdrv_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - int generated_co_wrapper bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, @@ -112,21 +85,4 @@ bdrv_common_block_status_above(BlockDriverState *bs, int generated_co_wrapper nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); -int generated_co_wrapper -blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - -int generated_co_wrapper -blk_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); - -int generated_co_wrapper -blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); - -int generated_co_wrapper -blk_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); - -int generated_co_wrapper blk_do_flush(BlockBackend *blk); - #endif /* BLOCK_COROUTINES_H */ diff --git a/block/crypto.c b/block/crypto.c index 1ba82984efe..7a57774b768 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -55,40 +55,40 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format, } -static ssize_t block_crypto_read_func(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int block_crypto_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { BlockDriverState *bs = opaque; ssize_t ret; - ret = bdrv_pread(bs->file, offset, buf, buflen); + ret = bdrv_pread(bs->file, offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return ret; } - return ret; + return 0; } -static ssize_t block_crypto_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int block_crypto_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { BlockDriverState *bs = opaque; ssize_t ret; - ret = bdrv_pwrite(bs->file, offset, buf, buflen); + ret = bdrv_pwrite(bs->file, offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); return ret; } - return ret; + return 0; } @@ -99,28 +99,28 @@ struct BlockCryptoCreateData { }; -static ssize_t block_crypto_create_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int block_crypto_create_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { struct BlockCryptoCreateData *data = opaque; ssize_t ret; - ret = blk_pwrite(data->blk, offset, buf, buflen, 0); + ret = blk_pwrite(data->blk, offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); return ret; } - return ret; + return 0; } -static ssize_t block_crypto_create_init_func(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp) +static int block_crypto_create_init_func(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp) { struct BlockCryptoCreateData *data = opaque; Error *local_error = NULL; @@ -139,7 +139,7 @@ static ssize_t block_crypto_create_init_func(QCryptoBlock *block, data->prealloc, 0, &local_error); if (ret >= 0) { - return ret; + return 0; } error: diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index da1b91166f4..bf3dc0512a6 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -309,10 +309,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent, return NULL; } - if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { - error_setg(errp, "Merging of parent and successor bitmap failed"); - return NULL; - } + hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap); parent->disabled = successor->disabled; parent->busy = false; @@ -912,13 +909,15 @@ bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, goto out; } - if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { - error_setg(errp, "Bitmaps are incompatible and can't be merged"); + if (bdrv_dirty_bitmap_size(src) != bdrv_dirty_bitmap_size(dest)) { + error_setg(errp, "Bitmaps are of different sizes (destination size is %" + PRId64 ", source size is %" PRId64 ") and can't be merged", + bdrv_dirty_bitmap_size(dest), bdrv_dirty_bitmap_size(src)); goto out; } - ret = bdrv_dirty_bitmap_merge_internal(dest, src, backup, false); - assert(ret); + bdrv_dirty_bitmap_merge_internal(dest, src, backup, false); + ret = true; out: bdrv_dirty_bitmaps_unlock(dest->bs); @@ -932,17 +931,16 @@ bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, /** * bdrv_dirty_bitmap_merge_internal: merge src into dest. * Does NOT check bitmap permissions; not suitable for use as public API. + * @dest, @src and @backup (if not NULL) must have same size. * * @backup: If provided, make a copy of dest here prior to merge. * @lock: If true, lock and unlock bitmaps on the way in/out. - * returns true if the merge succeeded; false if unattempted. */ -bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, +void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, bool lock) { - bool ret; IO_CODE(); assert(!bdrv_dirty_bitmap_readonly(dest)); @@ -959,9 +957,9 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, if (backup) { *backup = dest->bitmap; dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); - ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); + hbitmap_merge(*backup, src->bitmap, dest->bitmap); } else { - ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); + hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); } if (lock) { @@ -970,6 +968,4 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, bdrv_dirty_bitmaps_unlock(src->bs); } } - - return ret; } diff --git a/block/dmg.c b/block/dmg.c index c626587f9c5..98db18d82a0 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -77,7 +77,7 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) uint64_t buffer; int ret; - ret = bdrv_pread(bs->file, offset, &buffer, 8); + ret = bdrv_pread(bs->file, offset, 8, &buffer, 0); if (ret < 0) { return ret; } @@ -91,7 +91,7 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) uint32_t buffer; int ret; - ret = bdrv_pread(bs->file, offset, &buffer, 4); + ret = bdrv_pread(bs->file, offset, 4, &buffer, 0); if (ret < 0) { return ret; } @@ -172,7 +172,7 @@ static int64_t dmg_find_koly_offset(BdrvChild *file, Error **errp) offset = length - 511 - 512; } length = length < 515 ? length : 515; - ret = bdrv_pread(file, offset, buffer, length); + ret = bdrv_pread(file, offset, length, buffer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed while reading UDIF trailer"); return ret; @@ -352,7 +352,7 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, offset += 4; buffer = g_realloc(buffer, count); - ret = bdrv_pread(bs->file, offset, buffer, count); + ret = bdrv_pread(bs->file, offset, count, buffer, 0); if (ret < 0) { goto fail; } @@ -389,8 +389,8 @@ static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, buffer = g_malloc(info_length + 1); buffer[info_length] = '\0'; - ret = bdrv_pread(bs->file, info_begin, buffer, info_length); - if (ret != info_length) { + ret = bdrv_pread(bs->file, info_begin, info_length, buffer, 0); + if (ret < 0) { ret = -EINVAL; goto fail; } @@ -609,9 +609,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) case UDZO: { /* zlib compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); + if (ret < 0) { return -1; } @@ -635,9 +635,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); + if (ret < 0) { return -1; } @@ -656,9 +656,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); + if (ret < 0) { return -1; } @@ -672,9 +672,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } break; case UDRW: /* copy */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->uncompressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->uncompressed_chunk, 0); + if (ret < 0) { return -1; } break; diff --git a/block/export/export.c b/block/export/export.c index 7253af3bc3d..47448629155 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -26,6 +26,9 @@ #ifdef CONFIG_VHOST_USER_BLK_SERVER #include "vhost-user-blk-server.h" #endif +#ifdef CONFIG_VDUSE_BLK_EXPORT +#include "vduse-blk.h" +#endif static const BlockExportDriver *blk_exp_drivers[] = { &blk_exp_nbd, @@ -35,6 +38,9 @@ static const BlockExportDriver *blk_exp_drivers[] = { #ifdef CONFIG_FUSE &blk_exp_fuse, #endif +#ifdef CONFIG_VDUSE_BLK_EXPORT + &blk_exp_vduse_blk, +#endif }; /* Only accessed from the main thread */ diff --git a/block/export/fuse.c b/block/export/fuse.c index e80b24a8671..1b26ddfcf3c 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -554,7 +554,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t inode, return; } - ret = blk_pread(exp->common.blk, offset, buf, size); + ret = blk_pread(exp->common.blk, offset, size, buf, 0); if (ret >= 0) { fuse_reply_buf(req, buf, size); } else { @@ -607,7 +607,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, } } - ret = blk_pwrite(exp->common.blk, offset, buf, size, 0); + ret = blk_pwrite(exp->common.blk, offset, size, buf, 0); if (ret >= 0) { fuse_reply_write(req, size); } else { diff --git a/block/export/meson.build b/block/export/meson.build index 0a08e384c74..c60116f455a 100644 --- a/block/export/meson.build +++ b/block/export/meson.build @@ -1,7 +1,12 @@ blockdev_ss.add(files('export.c')) if have_vhost_user_blk_server - blockdev_ss.add(files('vhost-user-blk-server.c')) + blockdev_ss.add(files('vhost-user-blk-server.c', 'virtio-blk-handler.c')) endif blockdev_ss.add(when: fuse, if_true: files('fuse.c')) + +if have_vduse_blk_export + blockdev_ss.add(files('vduse-blk.c', 'virtio-blk-handler.c')) + blockdev_ss.add(libvduse) +endif diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c new file mode 100644 index 00000000000..f101c24c3f1 --- /dev/null +++ b/block/export/vduse-blk.c @@ -0,0 +1,374 @@ +/* + * Export QEMU block device via VDUSE + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/export.h" +#include "qemu/error-report.h" +#include "util/block-helpers.h" +#include "subprojects/libvduse/libvduse.h" +#include "virtio-blk-handler.h" + +#include "standard-headers/linux/virtio_blk.h" + +#define VDUSE_DEFAULT_NUM_QUEUE 1 +#define VDUSE_DEFAULT_QUEUE_SIZE 256 + +typedef struct VduseBlkExport { + BlockExport export; + VirtioBlkHandler handler; + VduseDev *dev; + uint16_t num_queues; + char *recon_file; + unsigned int inflight; +} VduseBlkExport; + +typedef struct VduseBlkReq { + VduseVirtqElement elem; + VduseVirtq *vq; +} VduseBlkReq; + +static void vduse_blk_inflight_inc(VduseBlkExport *vblk_exp) +{ + vblk_exp->inflight++; +} + +static void vduse_blk_inflight_dec(VduseBlkExport *vblk_exp) +{ + if (--vblk_exp->inflight == 0) { + aio_wait_kick(); + } +} + +static void vduse_blk_req_complete(VduseBlkReq *req, size_t in_len) +{ + vduse_queue_push(req->vq, &req->elem, in_len); + vduse_queue_notify(req->vq); + + free(req); +} + +static void coroutine_fn vduse_blk_virtio_process_req(void *opaque) +{ + VduseBlkReq *req = opaque; + VduseVirtq *vq = req->vq; + VduseDev *dev = vduse_queue_get_dev(vq); + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + VirtioBlkHandler *handler = &vblk_exp->handler; + VduseVirtqElement *elem = &req->elem; + struct iovec *in_iov = elem->in_sg; + struct iovec *out_iov = elem->out_sg; + unsigned in_num = elem->in_num; + unsigned out_num = elem->out_num; + int in_len; + + in_len = virtio_blk_process_req(handler, in_iov, + out_iov, in_num, out_num); + if (in_len < 0) { + free(req); + return; + } + + vduse_blk_req_complete(req, in_len); + vduse_blk_inflight_dec(vblk_exp); +} + +static void vduse_blk_vq_handler(VduseDev *dev, VduseVirtq *vq) +{ + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + + while (1) { + VduseBlkReq *req; + + req = vduse_queue_pop(vq, sizeof(VduseBlkReq)); + if (!req) { + break; + } + req->vq = vq; + + Coroutine *co = + qemu_coroutine_create(vduse_blk_virtio_process_req, req); + + vduse_blk_inflight_inc(vblk_exp); + qemu_coroutine_enter(co); + } +} + +static void on_vduse_vq_kick(void *opaque) +{ + VduseVirtq *vq = opaque; + VduseDev *dev = vduse_queue_get_dev(vq); + int fd = vduse_queue_get_fd(vq); + eventfd_t kick_data; + + if (eventfd_read(fd, &kick_data) == -1) { + error_report("failed to read data from eventfd"); + return; + } + + vduse_blk_vq_handler(dev, vq); +} + +static void vduse_blk_enable_queue(VduseDev *dev, VduseVirtq *vq) +{ + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + + aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq), + true, on_vduse_vq_kick, NULL, NULL, NULL, vq); + /* Make sure we don't miss any kick afer reconnecting */ + eventfd_write(vduse_queue_get_fd(vq), 1); +} + +static void vduse_blk_disable_queue(VduseDev *dev, VduseVirtq *vq) +{ + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + + aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq), + true, NULL, NULL, NULL, NULL, NULL); +} + +static const VduseOps vduse_blk_ops = { + .enable_queue = vduse_blk_enable_queue, + .disable_queue = vduse_blk_disable_queue, +}; + +static void on_vduse_dev_kick(void *opaque) +{ + VduseDev *dev = opaque; + + vduse_dev_handler(dev); +} + +static void vduse_blk_attach_ctx(VduseBlkExport *vblk_exp, AioContext *ctx) +{ + int i; + + aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), + true, on_vduse_dev_kick, NULL, NULL, NULL, + vblk_exp->dev); + + for (i = 0; i < vblk_exp->num_queues; i++) { + VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); + int fd = vduse_queue_get_fd(vq); + + if (fd < 0) { + continue; + } + aio_set_fd_handler(vblk_exp->export.ctx, fd, true, + on_vduse_vq_kick, NULL, NULL, NULL, vq); + } +} + +static void vduse_blk_detach_ctx(VduseBlkExport *vblk_exp) +{ + int i; + + for (i = 0; i < vblk_exp->num_queues; i++) { + VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); + int fd = vduse_queue_get_fd(vq); + + if (fd < 0) { + continue; + } + aio_set_fd_handler(vblk_exp->export.ctx, fd, + true, NULL, NULL, NULL, NULL, NULL); + } + aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), + true, NULL, NULL, NULL, NULL, NULL); + + AIO_WAIT_WHILE(vblk_exp->export.ctx, vblk_exp->inflight > 0); +} + + +static void blk_aio_attached(AioContext *ctx, void *opaque) +{ + VduseBlkExport *vblk_exp = opaque; + + vblk_exp->export.ctx = ctx; + vduse_blk_attach_ctx(vblk_exp, ctx); +} + +static void blk_aio_detach(void *opaque) +{ + VduseBlkExport *vblk_exp = opaque; + + vduse_blk_detach_ctx(vblk_exp); + vblk_exp->export.ctx = NULL; +} + +static void vduse_blk_resize(void *opaque) +{ + BlockExport *exp = opaque; + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + struct virtio_blk_config config; + + config.capacity = + cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS); + vduse_dev_update_config(vblk_exp->dev, sizeof(config.capacity), + offsetof(struct virtio_blk_config, capacity), + (char *)&config.capacity); +} + +static const BlockDevOps vduse_block_ops = { + .resize_cb = vduse_blk_resize, +}; + +static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, + Error **errp) +{ + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + BlockExportOptionsVduseBlk *vblk_opts = &opts->u.vduse_blk; + uint64_t logical_block_size = VIRTIO_BLK_SECTOR_SIZE; + uint16_t num_queues = VDUSE_DEFAULT_NUM_QUEUE; + uint16_t queue_size = VDUSE_DEFAULT_QUEUE_SIZE; + Error *local_err = NULL; + struct virtio_blk_config config = { 0 }; + uint64_t features; + int i, ret; + + if (vblk_opts->has_num_queues) { + num_queues = vblk_opts->num_queues; + if (num_queues == 0) { + error_setg(errp, "num-queues must be greater than 0"); + return -EINVAL; + } + } + + if (vblk_opts->has_queue_size) { + queue_size = vblk_opts->queue_size; + if (queue_size <= 2 || !is_power_of_2(queue_size) || + queue_size > VIRTQUEUE_MAX_SIZE) { + error_setg(errp, "queue-size is invalid"); + return -EINVAL; + } + } + + if (vblk_opts->has_logical_block_size) { + logical_block_size = vblk_opts->logical_block_size; + check_block_size(exp->id, "logical-block-size", logical_block_size, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + } + vblk_exp->num_queues = num_queues; + vblk_exp->handler.blk = exp->blk; + vblk_exp->handler.serial = g_strdup(vblk_opts->has_serial ? + vblk_opts->serial : ""); + vblk_exp->handler.logical_block_size = logical_block_size; + vblk_exp->handler.writable = opts->writable; + + config.capacity = + cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS); + config.seg_max = cpu_to_le32(queue_size - 2); + config.min_io_size = cpu_to_le16(1); + config.opt_io_size = cpu_to_le32(1); + config.num_queues = cpu_to_le16(num_queues); + config.blk_size = cpu_to_le32(logical_block_size); + config.max_discard_sectors = cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS); + config.max_discard_seg = cpu_to_le32(1); + config.discard_sector_alignment = + cpu_to_le32(logical_block_size >> VIRTIO_BLK_SECTOR_BITS); + config.max_write_zeroes_sectors = + cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS); + config.max_write_zeroes_seg = cpu_to_le32(1); + + features = vduse_get_virtio_features() | + (1ULL << VIRTIO_BLK_F_SEG_MAX) | + (1ULL << VIRTIO_BLK_F_TOPOLOGY) | + (1ULL << VIRTIO_BLK_F_BLK_SIZE) | + (1ULL << VIRTIO_BLK_F_FLUSH) | + (1ULL << VIRTIO_BLK_F_DISCARD) | + (1ULL << VIRTIO_BLK_F_WRITE_ZEROES); + + if (num_queues > 1) { + features |= 1ULL << VIRTIO_BLK_F_MQ; + } + if (!opts->writable) { + features |= 1ULL << VIRTIO_BLK_F_RO; + } + + vblk_exp->dev = vduse_dev_create(vblk_opts->name, VIRTIO_ID_BLOCK, 0, + features, num_queues, + sizeof(struct virtio_blk_config), + (char *)&config, &vduse_blk_ops, + vblk_exp); + if (!vblk_exp->dev) { + error_setg(errp, "failed to create vduse device"); + ret = -ENOMEM; + goto err_dev; + } + + vblk_exp->recon_file = g_strdup_printf("%s/vduse-blk-%s", + g_get_tmp_dir(), vblk_opts->name); + if (vduse_set_reconnect_log_file(vblk_exp->dev, vblk_exp->recon_file)) { + error_setg(errp, "failed to set reconnect log file"); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < num_queues; i++) { + vduse_dev_setup_queue(vblk_exp->dev, i, queue_size); + } + + aio_set_fd_handler(exp->ctx, vduse_dev_get_fd(vblk_exp->dev), true, + on_vduse_dev_kick, NULL, NULL, NULL, vblk_exp->dev); + + blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vblk_exp); + + blk_set_dev_ops(exp->blk, &vduse_block_ops, exp); + + return 0; +err: + vduse_dev_destroy(vblk_exp->dev); + g_free(vblk_exp->recon_file); +err_dev: + g_free(vblk_exp->handler.serial); + return ret; +} + +static void vduse_blk_exp_delete(BlockExport *exp) +{ + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + int ret; + + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vblk_exp); + blk_set_dev_ops(exp->blk, NULL, NULL); + ret = vduse_dev_destroy(vblk_exp->dev); + if (ret != -EBUSY) { + unlink(vblk_exp->recon_file); + } + g_free(vblk_exp->recon_file); + g_free(vblk_exp->handler.serial); +} + +static void vduse_blk_exp_request_shutdown(BlockExport *exp) +{ + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + + aio_context_acquire(vblk_exp->export.ctx); + vduse_blk_detach_ctx(vblk_exp); + aio_context_acquire(vblk_exp->export.ctx); +} + +const BlockExportDriver blk_exp_vduse_blk = { + .type = BLOCK_EXPORT_TYPE_VDUSE_BLK, + .instance_size = sizeof(VduseBlkExport), + .create = vduse_blk_exp_create, + .delete = vduse_blk_exp_delete, + .request_shutdown = vduse_blk_exp_request_shutdown, +}; diff --git a/block/export/vduse-blk.h b/block/export/vduse-blk.h new file mode 100644 index 00000000000..c4eeb1b70ec --- /dev/null +++ b/block/export/vduse-blk.h @@ -0,0 +1,20 @@ +/* + * Export QEMU block device via VDUSE + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef VDUSE_BLK_H +#define VDUSE_BLK_H + +#include "block/export.h" + +extern const BlockExportDriver blk_exp_vduse_blk; + +#endif /* VDUSE_BLK_H */ diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index a129204c444..3409d9e02e6 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -17,31 +17,15 @@ #include "vhost-user-blk-server.h" #include "qapi/error.h" #include "qom/object_interfaces.h" -#include "sysemu/block-backend.h" #include "util/block-helpers.h" - -/* - * Sector units are 512 bytes regardless of the - * virtio_blk_config->blk_size value. - */ -#define VIRTIO_BLK_SECTOR_BITS 9 -#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS) +#include "virtio-blk-handler.h" enum { VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1, - VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768, - VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768, -}; -struct virtio_blk_inhdr { - unsigned char status; }; typedef struct VuBlkReq { VuVirtqElement elem; - int64_t sector_num; - size_t size; - struct virtio_blk_inhdr *in; - struct virtio_blk_outhdr out; VuServer *server; struct VuVirtq *vq; } VuBlkReq; @@ -50,248 +34,44 @@ typedef struct VuBlkReq { typedef struct { BlockExport export; VuServer vu_server; - uint32_t blk_size; + VirtioBlkHandler handler; QIOChannelSocket *sioc; struct virtio_blk_config blkcfg; - bool writable; } VuBlkExport; -static void vu_blk_req_complete(VuBlkReq *req) +static void vu_blk_req_complete(VuBlkReq *req, size_t in_len) { VuDev *vu_dev = &req->server->vu_dev; - /* IO size with 1 extra status byte */ - vu_queue_push(vu_dev, req->vq, &req->elem, req->size + 1); + vu_queue_push(vu_dev, req->vq, &req->elem, in_len); vu_queue_notify(vu_dev, req->vq); free(req); } -static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector, - size_t size) -{ - uint64_t nb_sectors; - uint64_t total_sectors; - - if (size % VIRTIO_BLK_SECTOR_SIZE) { - return false; - } - - nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS; - - QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE); - if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return false; - } - if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) { - return false; - } - blk_get_geometry(vexp->export.blk, &total_sectors); - if (sector > total_sectors || nb_sectors > total_sectors - sector) { - return false; - } - return true; -} - -static int coroutine_fn -vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov, - uint32_t iovcnt, uint32_t type) -{ - BlockBackend *blk = vexp->export.blk; - struct virtio_blk_discard_write_zeroes desc; - ssize_t size; - uint64_t sector; - uint32_t num_sectors; - uint32_t max_sectors; - uint32_t flags; - int bytes; - - /* Only one desc is currently supported */ - if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { - return VIRTIO_BLK_S_UNSUPP; - } - - size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); - if (unlikely(size != sizeof(desc))) { - error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); - return VIRTIO_BLK_S_IOERR; - } - - sector = le64_to_cpu(desc.sector); - num_sectors = le32_to_cpu(desc.num_sectors); - flags = le32_to_cpu(desc.flags); - max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? - VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS : - VHOST_USER_BLK_MAX_DISCARD_SECTORS; - - /* This check ensures that 'bytes' fits in an int */ - if (unlikely(num_sectors > max_sectors)) { - return VIRTIO_BLK_S_IOERR; - } - - bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; - - if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) { - return VIRTIO_BLK_S_IOERR; - } - - /* - * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard - * and write zeroes commands if any unknown flag is set. - */ - if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { - return VIRTIO_BLK_S_UNSUPP; - } - - if (type == VIRTIO_BLK_T_WRITE_ZEROES) { - int blk_flags = 0; - - if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { - blk_flags |= BDRV_REQ_MAY_UNMAP; - } - - if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, - bytes, blk_flags) == 0) { - return VIRTIO_BLK_S_OK; - } - } else if (type == VIRTIO_BLK_T_DISCARD) { - /* - * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for - * discard commands if the unmap flag is set. - */ - if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { - return VIRTIO_BLK_S_UNSUPP; - } - - if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, - bytes) == 0) { - return VIRTIO_BLK_S_OK; - } - } - - return VIRTIO_BLK_S_IOERR; -} - /* Called with server refcount increased, must decrease before returning */ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) { VuBlkReq *req = opaque; VuServer *server = req->server; VuVirtqElement *elem = &req->elem; - uint32_t type; - VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); - BlockBackend *blk = vexp->export.blk; - + VirtioBlkHandler *handler = &vexp->handler; struct iovec *in_iov = elem->in_sg; struct iovec *out_iov = elem->out_sg; unsigned in_num = elem->in_num; unsigned out_num = elem->out_num; - - /* refer to hw/block/virtio_blk.c */ - if (elem->out_num < 1 || elem->in_num < 1) { - error_report("virtio-blk request missing headers"); - goto err; - } - - if (unlikely(iov_to_buf(out_iov, out_num, 0, &req->out, - sizeof(req->out)) != sizeof(req->out))) { - error_report("virtio-blk request outhdr too short"); - goto err; - } - - iov_discard_front(&out_iov, &out_num, sizeof(req->out)); - - if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { - error_report("virtio-blk request inhdr too short"); - goto err; - } - - /* We always touch the last byte, so just see how big in_iov is. */ - req->in = (void *)in_iov[in_num - 1].iov_base - + in_iov[in_num - 1].iov_len - - sizeof(struct virtio_blk_inhdr); - iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - - type = le32_to_cpu(req->out.type); - switch (type & ~VIRTIO_BLK_T_BARRIER) { - case VIRTIO_BLK_T_IN: - case VIRTIO_BLK_T_OUT: { - QEMUIOVector qiov; - int64_t offset; - ssize_t ret = 0; - bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = le64_to_cpu(req->out.sector); - - if (is_write && !vexp->writable) { - req->in->status = VIRTIO_BLK_S_IOERR; - break; - } - - if (is_write) { - qemu_iovec_init_external(&qiov, out_iov, out_num); - } else { - qemu_iovec_init_external(&qiov, in_iov, in_num); - } - - if (unlikely(!vu_blk_sect_range_ok(vexp, - req->sector_num, - qiov.size))) { - req->in->status = VIRTIO_BLK_S_IOERR; - break; - } - - offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS; - - if (is_write) { - ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); - } else { - ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); - } - if (ret >= 0) { - req->in->status = VIRTIO_BLK_S_OK; - } else { - req->in->status = VIRTIO_BLK_S_IOERR; - } - break; - } - case VIRTIO_BLK_T_FLUSH: - if (blk_co_flush(blk) == 0) { - req->in->status = VIRTIO_BLK_S_OK; - } else { - req->in->status = VIRTIO_BLK_S_IOERR; - } - break; - case VIRTIO_BLK_T_GET_ID: { - size_t size = MIN(iov_size(&elem->in_sg[0], in_num), - VIRTIO_BLK_ID_BYTES); - snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk"); - req->in->status = VIRTIO_BLK_S_OK; - req->size = elem->in_sg[0].iov_len; - break; + int in_len; + + in_len = virtio_blk_process_req(handler, in_iov, out_iov, + in_num, out_num); + if (in_len < 0) { + free(req); + vhost_user_server_unref(server); + return; } - case VIRTIO_BLK_T_DISCARD: - case VIRTIO_BLK_T_WRITE_ZEROES: { - if (!vexp->writable) { - req->in->status = VIRTIO_BLK_S_IOERR; - break; - } - - req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num, - type); - break; - } - default: - req->in->status = VIRTIO_BLK_S_UNSUPP; - break; - } - - vu_blk_req_complete(req); - vhost_user_server_unref(server); - return; -err: - free(req); + vu_blk_req_complete(req, in_len); vhost_user_server_unref(server); } @@ -348,7 +128,7 @@ static uint64_t vu_blk_get_features(VuDev *dev) 1ull << VIRTIO_RING_F_EVENT_IDX | 1ull << VHOST_USER_F_PROTOCOL_FEATURES; - if (!vexp->writable) { + if (!vexp->handler.writable) { features |= 1ull << VIRTIO_BLK_F_RO; } @@ -455,12 +235,12 @@ vu_blk_initialize_config(BlockDriverState *bs, config->opt_io_size = cpu_to_le32(1); config->num_queues = cpu_to_le16(num_queues); config->max_discard_sectors = - cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS); + cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS); config->max_discard_seg = cpu_to_le32(1); config->discard_sector_alignment = cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS); config->max_write_zeroes_sectors - = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS); + = cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS); config->max_write_zeroes_seg = cpu_to_le32(1); } @@ -480,7 +260,6 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, uint64_t logical_block_size; uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT; - vexp->writable = opts->writable; vexp->blkcfg.wce = 0; if (vu_opts->has_logical_block_size) { @@ -494,8 +273,6 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, error_propagate(errp, local_err); return -EINVAL; } - vexp->blk_size = logical_block_size; - blk_set_guest_block_size(exp->blk, logical_block_size); if (vu_opts->has_num_queues) { num_queues = vu_opts->num_queues; @@ -504,6 +281,10 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, error_setg(errp, "num-queues must be greater than 0"); return -EINVAL; } + vexp->handler.blk = exp->blk; + vexp->handler.serial = g_strdup("vhost_user_blk"); + vexp->handler.logical_block_size = logical_block_size; + vexp->handler.writable = opts->writable; vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg, logical_block_size, num_queues); @@ -515,6 +296,7 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, num_queues, &vu_blk_iface, errp)) { blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, vexp); + g_free(vexp->handler.serial); return -EADDRNOTAVAIL; } @@ -527,6 +309,7 @@ static void vu_blk_exp_delete(BlockExport *exp) blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, vexp); + g_free(vexp->handler.serial); } const BlockExportDriver blk_exp_vhost_user_blk = { diff --git a/block/export/virtio-blk-handler.c b/block/export/virtio-blk-handler.c new file mode 100644 index 00000000000..313666e8ab9 --- /dev/null +++ b/block/export/virtio-blk-handler.c @@ -0,0 +1,240 @@ +/* + * Handler for virtio-blk I/O + * + * Copyright (c) 2020 Red Hat, Inc. + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Coiby Xu + * Xie Yongji + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "virtio-blk-handler.h" + +#include "standard-headers/linux/virtio_blk.h" + +struct virtio_blk_inhdr { + unsigned char status; +}; + +static bool virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size, + uint64_t sector, size_t size) +{ + uint64_t nb_sectors; + uint64_t total_sectors; + + if (size % VIRTIO_BLK_SECTOR_SIZE) { + return false; + } + + nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS; + + QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE); + if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return false; + } + if ((sector << VIRTIO_BLK_SECTOR_BITS) % block_size) { + return false; + } + blk_get_geometry(blk, &total_sectors); + if (sector > total_sectors || nb_sectors > total_sectors - sector) { + return false; + } + return true; +} + +static int coroutine_fn +virtio_blk_discard_write_zeroes(VirtioBlkHandler *handler, struct iovec *iov, + uint32_t iovcnt, uint32_t type) +{ + BlockBackend *blk = handler->blk; + struct virtio_blk_discard_write_zeroes desc; + ssize_t size; + uint64_t sector; + uint32_t num_sectors; + uint32_t max_sectors; + uint32_t flags; + int bytes; + + /* Only one desc is currently supported */ + if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { + return VIRTIO_BLK_S_UNSUPP; + } + + size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); + if (unlikely(size != sizeof(desc))) { + error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); + return VIRTIO_BLK_S_IOERR; + } + + sector = le64_to_cpu(desc.sector); + num_sectors = le32_to_cpu(desc.num_sectors); + flags = le32_to_cpu(desc.flags); + max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? + VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS : + VIRTIO_BLK_MAX_DISCARD_SECTORS; + + /* This check ensures that 'bytes' fits in an int */ + if (unlikely(num_sectors > max_sectors)) { + return VIRTIO_BLK_S_IOERR; + } + + bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; + + if (unlikely(!virtio_blk_sect_range_ok(blk, handler->logical_block_size, + sector, bytes))) { + return VIRTIO_BLK_S_IOERR; + } + + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard + * and write zeroes commands if any unknown flag is set. + */ + if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + int blk_flags = 0; + + if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + blk_flags |= BDRV_REQ_MAY_UNMAP; + } + + if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes, blk_flags) == 0) { + return VIRTIO_BLK_S_OK; + } + } else if (type == VIRTIO_BLK_T_DISCARD) { + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for + * discard commands if the unmap flag is set. + */ + if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes) == 0) { + return VIRTIO_BLK_S_OK; + } + } + + return VIRTIO_BLK_S_IOERR; +} + +int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler, + struct iovec *in_iov, + struct iovec *out_iov, + unsigned int in_num, + unsigned int out_num) +{ + BlockBackend *blk = handler->blk; + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr out; + uint32_t type; + int in_len; + + if (out_num < 1 || in_num < 1) { + error_report("virtio-blk request missing headers"); + return -EINVAL; + } + + if (unlikely(iov_to_buf(out_iov, out_num, 0, &out, + sizeof(out)) != sizeof(out))) { + error_report("virtio-blk request outhdr too short"); + return -EINVAL; + } + + iov_discard_front(&out_iov, &out_num, sizeof(out)); + + if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { + error_report("virtio-blk request inhdr too short"); + return -EINVAL; + } + + /* We always touch the last byte, so just see how big in_iov is. */ + in_len = iov_size(in_iov, in_num); + in = (void *)in_iov[in_num - 1].iov_base + + in_iov[in_num - 1].iov_len + - sizeof(struct virtio_blk_inhdr); + iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); + + type = le32_to_cpu(out.type); + switch (type & ~VIRTIO_BLK_T_BARRIER) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: { + QEMUIOVector qiov; + int64_t offset; + ssize_t ret = 0; + bool is_write = type & VIRTIO_BLK_T_OUT; + int64_t sector_num = le64_to_cpu(out.sector); + + if (is_write && !handler->writable) { + in->status = VIRTIO_BLK_S_IOERR; + break; + } + + if (is_write) { + qemu_iovec_init_external(&qiov, out_iov, out_num); + } else { + qemu_iovec_init_external(&qiov, in_iov, in_num); + } + + if (unlikely(!virtio_blk_sect_range_ok(blk, + handler->logical_block_size, + sector_num, qiov.size))) { + in->status = VIRTIO_BLK_S_IOERR; + break; + } + + offset = sector_num << VIRTIO_BLK_SECTOR_BITS; + + if (is_write) { + ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); + } else { + ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); + } + if (ret >= 0) { + in->status = VIRTIO_BLK_S_OK; + } else { + in->status = VIRTIO_BLK_S_IOERR; + } + break; + } + case VIRTIO_BLK_T_FLUSH: + if (blk_co_flush(blk) == 0) { + in->status = VIRTIO_BLK_S_OK; + } else { + in->status = VIRTIO_BLK_S_IOERR; + } + break; + case VIRTIO_BLK_T_GET_ID: { + size_t size = MIN(strlen(handler->serial) + 1, + MIN(iov_size(in_iov, in_num), + VIRTIO_BLK_ID_BYTES)); + iov_from_buf(in_iov, in_num, 0, handler->serial, size); + in->status = VIRTIO_BLK_S_OK; + break; + } + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: + if (!handler->writable) { + in->status = VIRTIO_BLK_S_IOERR; + break; + } + in->status = virtio_blk_discard_write_zeroes(handler, out_iov, + out_num, type); + break; + default: + in->status = VIRTIO_BLK_S_UNSUPP; + break; + } + + return in_len; +} diff --git a/block/export/virtio-blk-handler.h b/block/export/virtio-blk-handler.h new file mode 100644 index 00000000000..150d44cff24 --- /dev/null +++ b/block/export/virtio-blk-handler.h @@ -0,0 +1,37 @@ +/* + * Handler for virtio-blk I/O + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef VIRTIO_BLK_HANDLER_H +#define VIRTIO_BLK_HANDLER_H + +#include "sysemu/block-backend.h" + +#define VIRTIO_BLK_SECTOR_BITS 9 +#define VIRTIO_BLK_SECTOR_SIZE (1ULL << VIRTIO_BLK_SECTOR_BITS) + +#define VIRTIO_BLK_MAX_DISCARD_SECTORS 32768 +#define VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS 32768 + +typedef struct { + BlockBackend *blk; + char *serial; + uint32_t logical_block_size; + bool writable; +} VirtioBlkHandler; + +int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler, + struct iovec *in_iov, + struct iovec *out_iov, + unsigned int in_num, + unsigned int out_num); + +#endif /* VIRTIO_BLK_HANDLER_H */ diff --git a/block/gluster.c b/block/gluster.c index 398976bc66d..b60213ab804 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -891,7 +891,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp) { bs->bl.max_transfer = GLUSTER_MAX_TRANSFER; - bs->bl.max_pdiscard = SIZE_MAX; + bs->bl.max_pdiscard = MIN(SIZE_MAX, INT64_MAX); } static int qemu_gluster_reopen_prepare(BDRVReopenState *state, diff --git a/block/io.c b/block/io.c index 789e6373d5a..0a8cbefe86e 100644 --- a/block/io.c +++ b/block/io.c @@ -588,21 +588,6 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) BDRV_POLL_WHILE(child->bs, qatomic_read(&drained_end_counter) > 0); } -/* - * Wait for pending requests to complete on a single BlockDriverState subtree, - * and suspend block driver's internal I/O until next request arrives. - * - * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState - * AioContext. - */ -void coroutine_fn bdrv_co_drain(BlockDriverState *bs) -{ - IO_OR_GS_CODE(); - assert(qemu_in_coroutine()); - bdrv_drained_begin(bs); - bdrv_drained_end(bs); -} - void bdrv_drain(BlockDriverState *bs) { IO_OR_GS_CODE(); @@ -1061,14 +1046,6 @@ static int bdrv_check_request32(int64_t offset, int64_t bytes, return 0; } -int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) -{ - IO_CODE(); - return bdrv_pwritev(child, offset, bytes, NULL, - BDRV_REQ_ZERO_WRITE | flags); -} - /* * Completely zero out a block device with the help of bdrv_pwrite_zeroes. * The operation is sped up by checking the block status and only writing @@ -1111,62 +1088,25 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) } } -/* See bdrv_pwrite() for the return codes */ -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_CODE(); - - if (bytes < 0) { - return -EINVAL; - } - - ret = bdrv_preadv(child, offset, bytes, &qiov, 0); - - return ret < 0 ? ret : bytes; -} - -/* Return no. of bytes on success or < 0 on error. Important errors are: - -EIO generic I/O error (may happen for all errors) - -ENOMEDIUM No media inserted. - -EINVAL Invalid offset or number of bytes - -EACCES Trying to write a read-only device -*/ -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, - int64_t bytes) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_CODE(); - - if (bytes < 0) { - return -EINVAL; - } - - ret = bdrv_pwritev(child, offset, bytes, &qiov, 0); - - return ret < 0 ? ret : bytes; -} - /* * Writes to the file and ensures that no writes are reordered across this * request (acts as a barrier) * * Returns 0 on success, -errno in error cases. */ -int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int64_t count) +int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags) { int ret; IO_CODE(); - ret = bdrv_pwrite(child, offset, buf, count); + ret = bdrv_co_pwrite(child, offset, bytes, buf, flags); if (ret < 0) { return ret; } - ret = bdrv_flush(child->bs); + ret = bdrv_co_flush(child->bs); if (ret < 0) { return ret; } diff --git a/block/io_uring.c b/block/io_uring.c index d48e472e74c..f8a19fd97f9 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -73,12 +73,8 @@ static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb) /** * luring_resubmit_short_read: * - * Before Linux commit 9d93a3f5a0c ("io_uring: punt short reads to async - * context") a buffered I/O request with the start of the file range in the - * page cache could result in a short read. Applications need to resubmit the - * remaining read request. - * - * This is a slow path but recent kernels never take it. + * Short reads are rare but may occur. The remaining read request needs to be + * resubmitted. */ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, int nread) @@ -89,7 +85,7 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, trace_luring_resubmit_short_read(s, luringcb, nread); /* Update read position */ - luringcb->total_read = nread; + luringcb->total_read += nread; remaining = luringcb->qiov->size - luringcb->total_read; /* Shorten qiov */ @@ -103,7 +99,7 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, remaining); /* Update sqe */ - luringcb->sqeq.off = nread; + luringcb->sqeq.off += nread; luringcb->sqeq.addr = (__u64)(uintptr_t)luringcb->resubmit_qiov.iov; luringcb->sqeq.len = luringcb->resubmit_qiov.niov; diff --git a/block/meson.build b/block/meson.build index 0b2a60c99b7..60bc3055972 100644 --- a/block/meson.build +++ b/block/meson.build @@ -136,6 +136,7 @@ block_gen_c = custom_target('block-gen.c', input: files( '../include/block/block-io.h', '../include/block/block-global-state.h', + '../include/sysemu/block-backend-io.h', 'coroutines.h' ), command: [wrapper_py, '@OUTPUT@', '@INPUT@']) diff --git a/block/mirror.c b/block/mirror.c index d8ecb9efa29..3c4ab1159de 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -73,7 +73,7 @@ typedef struct MirrorBlockJob { uint64_t last_pause_ns; unsigned long *in_flight_bitmap; - int in_flight; + unsigned in_flight; int64_t bytes_in_flight; QTAILQ_HEAD(, MirrorOp) ops_in_flight; int ret; diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index 2b677c4a2ff..282363606f9 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -261,8 +261,9 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, HBitmap **backup, Error **errp) { BlockDriverState *bs; - BdrvDirtyBitmap *dst, *src, *anon; + BdrvDirtyBitmap *dst, *src; BlockDirtyBitmapOrStrList *lst; + HBitmap *local_backup = NULL; GLOBAL_STATE_CODE(); @@ -271,12 +272,6 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, return NULL; } - anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst), - NULL, errp); - if (!anon) { - return NULL; - } - for (lst = bms; lst; lst = lst->next) { switch (lst->value->type) { const char *name, *node; @@ -285,8 +280,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, src = bdrv_find_dirty_bitmap(bs, name); if (!src) { error_setg(errp, "Dirty bitmap '%s' not found", name); - dst = NULL; - goto out; + goto fail; } break; case QTYPE_QDICT: @@ -294,26 +288,36 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, name = lst->value->u.external.name; src = block_dirty_bitmap_lookup(node, name, NULL, errp); if (!src) { - dst = NULL; - goto out; + goto fail; } break; default: abort(); } - if (!bdrv_merge_dirty_bitmap(anon, src, NULL, errp)) { - dst = NULL; - goto out; + /* We do backup only for first merge operation */ + if (!bdrv_merge_dirty_bitmap(dst, src, + local_backup ? NULL : &local_backup, + errp)) + { + goto fail; } } - /* Merge into dst; dst is unchanged on failure. */ - bdrv_merge_dirty_bitmap(dst, anon, backup, errp); + if (backup) { + *backup = local_backup; + } else { + hbitmap_free(local_backup); + } - out: - bdrv_release_dirty_bitmap(anon); return dst; + +fail: + if (local_backup) { + bdrv_restore_dirty_bitmap(dst, local_backup); + } + + return NULL; } void qmp_block_dirty_bitmap_merge(const char *node, const char *target, diff --git a/block/nbd.c b/block/nbd.c index 6085ab1d2c1..97683cce27e 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -77,7 +77,7 @@ typedef struct BDRVNBDState { QemuMutex requests_lock; NBDClientState state; CoQueue free_sema; - int in_flight; + unsigned in_flight; NBDClientRequest requests[MAX_NBD_REQUESTS]; QEMUTimer *reconnect_delay_timer; @@ -371,6 +371,7 @@ static bool nbd_client_connecting(BDRVNBDState *s) /* Called with s->requests_lock taken. */ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) { + int ret; bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; /* @@ -380,6 +381,8 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) assert(nbd_client_connecting(s)); assert(s->in_flight == 1); + trace_nbd_reconnect_attempt(s->bs->in_flight); + if (blocking && !s->reconnect_delay_timer) { /* * It's the first reconnect attempt after switching to @@ -401,7 +404,8 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) } qemu_mutex_unlock(&s->requests_lock); - nbd_co_do_establish_connection(s->bs, blocking, NULL); + ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); + trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); qemu_mutex_lock(&s->requests_lock); /* @@ -521,12 +525,8 @@ static int coroutine_fn nbd_co_send_request(BlockDriverState *bs, if (qiov) { qio_channel_set_cork(s->ioc, true); rc = nbd_send_request(s->ioc, request); - if (rc >= 0) { - if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, - NULL) < 0) { - rc = -EIO; - } - } else if (rc >= 0) { + if (rc >= 0 && qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, + NULL) < 0) { rc = -EIO; } qio_channel_set_cork(s->ioc, false); diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 5122f67ac28..c9dbbf5089e 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -93,8 +93,8 @@ static int parallels_load_bitmap_data(BlockDriverState *bs, if (entry == 1) { bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false); } else { - ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf, - s->cluster_size); + ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, + s->cluster_size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read bitmap data cluster"); @@ -286,7 +286,7 @@ int parallels_read_format_extension(BlockDriverState *bs, assert(ext_off > 0); - ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size); + ret = bdrv_pread(bs->file, ext_off, s->cluster_size, ext_cluster, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read Format Extension cluster"); goto out; diff --git a/block/parallels.c b/block/parallels.c index 8879b7027a1..8b235b95055 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -277,8 +277,8 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) if (off + to_write > s->header_size) { to_write = s->header_size - off; } - ret = bdrv_pwrite(bs->file, off, (uint8_t *)s->header + off, - to_write); + ret = bdrv_pwrite(bs->file, off, to_write, (uint8_t *)s->header + off, + 0); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); return ret; @@ -481,7 +481,7 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, ret = 0; if (flush_bat) { - ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size); + ret = bdrv_co_pwrite_sync(bs->file, 0, s->header_size, s->header, 0); if (ret < 0) { res->check_errors++; goto out; @@ -599,7 +599,7 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, memset(tmp, 0, sizeof(tmp)); memcpy(tmp, &header, sizeof(header)); - ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0); + ret = blk_pwrite(blk, 0, BDRV_SECTOR_SIZE, tmp, 0); if (ret < 0) { goto exit; } @@ -723,7 +723,7 @@ static int parallels_update_header(BlockDriverState *bs) if (size > s->header_size) { size = s->header_size; } - return bdrv_pwrite_sync(bs->file, 0, s->header, size); + return bdrv_pwrite_sync(bs->file, 0, size, s->header, 0); } static int parallels_open(BlockDriverState *bs, QDict *options, int flags, @@ -742,7 +742,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); + ret = bdrv_pread(bs->file, 0, sizeof(ph), &ph, 0); if (ret < 0) { goto fail; } @@ -798,7 +798,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->header_size = size; } - ret = bdrv_pread(bs->file, 0, s->header, s->header_size); + ret = bdrv_pread(bs->file, 0, s->header_size, s->header, 0); if (ret < 0) { goto fail; } diff --git a/block/qcow.c b/block/qcow.c index 4fba1b9e364..311aaa87052 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -128,7 +128,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { goto fail; } @@ -260,8 +260,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, - s->l1_size * sizeof(uint64_t)); + ret = bdrv_pread(bs->file, s->l1_table_offset, + s->l1_size * sizeof(uint64_t), s->l1_table, 0); if (ret < 0) { goto fail; } @@ -291,8 +291,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file, header.backing_file_offset, - bs->auto_backing_file, len); + ret = bdrv_pread(bs->file, header.backing_file_offset, len, + bs->auto_backing_file, 0); if (ret < 0) { goto fail; } @@ -383,7 +383,7 @@ static int get_cluster_offset(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + l1_index * sizeof(tmp), - &tmp, sizeof(tmp)); + sizeof(tmp), &tmp, 0); if (ret < 0) { return ret; } @@ -414,14 +414,14 @@ static int get_cluster_offset(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); if (new_l2_table) { memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + ret = bdrv_pwrite_sync(bs->file, l2_offset, + s->l2_size * sizeof(uint64_t), l2_table, 0); if (ret < 0) { return ret; } } else { - ret = bdrv_pread(bs->file, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + ret = bdrv_pread(bs->file, l2_offset, s->l2_size * sizeof(uint64_t), + l2_table, 0); if (ret < 0) { return ret; } @@ -453,8 +453,8 @@ static int get_cluster_offset(BlockDriverState *bs, cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size); /* write the cluster content */ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, - s->cluster_size); + ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_size, + s->cluster_cache, 0); if (ret < 0) { return ret; } @@ -492,10 +492,9 @@ static int get_cluster_offset(BlockDriverState *bs, return -EIO; } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_pwrite(bs->file, - cluster_offset + i, - s->cluster_data, - BDRV_SECTOR_SIZE); + ret = bdrv_pwrite(bs->file, cluster_offset + i, + BDRV_SECTOR_SIZE, + s->cluster_data, 0); if (ret < 0) { return ret; } @@ -516,7 +515,7 @@ static int get_cluster_offset(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); } ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), - &tmp, sizeof(tmp)); + sizeof(tmp), &tmp, 0); if (ret < 0) { return ret; } @@ -597,8 +596,8 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) csize = cluster_offset >> (63 - s->cluster_bits); csize &= (s->cluster_size - 1); BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); - if (ret != csize) + ret = bdrv_pread(bs->file, coffset, csize, s->cluster_data, 0); + if (ret < 0) return -1; if (decompress_buffer(s->cluster_cache, s->cluster_size, s->cluster_data, csize) < 0) { @@ -891,15 +890,15 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, } /* write all the data */ - ret = blk_pwrite(qcow_blk, 0, &header, sizeof(header), 0); - if (ret != sizeof(header)) { + ret = blk_pwrite(qcow_blk, 0, sizeof(header), &header, 0); + if (ret < 0) { goto exit; } if (qcow_opts->has_backing_file) { - ret = blk_pwrite(qcow_blk, sizeof(header), - qcow_opts->backing_file, backing_filename_len, 0); - if (ret != backing_filename_len) { + ret = blk_pwrite(qcow_blk, sizeof(header), backing_filename_len, + qcow_opts->backing_file, 0); + if (ret < 0) { goto exit; } } @@ -908,8 +907,8 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, for (i = 0; i < DIV_ROUND_UP(sizeof(uint64_t) * l1_size, BDRV_SECTOR_SIZE); i++) { ret = blk_pwrite(qcow_blk, header_size + BDRV_SECTOR_SIZE * i, - tmp, BDRV_SECTOR_SIZE, 0); - if (ret != BDRV_SECTOR_SIZE) { + BDRV_SECTOR_SIZE, tmp, 0); + if (ret < 0) { g_free(tmp); goto exit; } @@ -1030,8 +1029,8 @@ static int qcow_make_empty(BlockDriverState *bs) int ret; memset(s->l1_table, 0, l1_length); - if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, - l1_length) < 0) + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, l1_length, s->l1_table, + 0) < 0) return -1; ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length, false, PREALLOC_MODE_OFF, 0, NULL); diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 8fb47315515..e98bafe0f4e 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -234,8 +234,8 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, } assert(tb->size <= BME_MAX_TABLE_SIZE); - ret = bdrv_pread(bs->file, tb->offset, - table, tb->size * BME_TABLE_ENTRY_SIZE); + ret = bdrv_pread(bs->file, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, + table, 0); if (ret < 0) { goto fail; } @@ -317,7 +317,7 @@ static int load_bitmap_data(BlockDriverState *bs, * already cleared */ } } else { - ret = bdrv_pread(bs->file, data_offset, buf, s->cluster_size); + ret = bdrv_pread(bs->file, data_offset, s->cluster_size, buf, 0); if (ret < 0) { goto finish; } @@ -575,7 +575,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, } dir_end = dir + size; - ret = bdrv_pread(bs->file, offset, dir, size); + ret = bdrv_pread(bs->file, offset, size, dir, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read bitmap directory"); goto fail; @@ -798,7 +798,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, goto fail; } - ret = bdrv_pwrite(bs->file, dir_offset, dir, dir_size); + ret = bdrv_pwrite(bs->file, dir_offset, dir_size, dir, 0); if (ret < 0) { goto fail; } @@ -1339,7 +1339,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } - ret = bdrv_pwrite(bs->file, off, buf, s->cluster_size); + ret = bdrv_pwrite(bs->file, off, s->cluster_size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", bm_name); @@ -1402,7 +1402,7 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) } bitmap_table_to_be(tb, tb_size); - ret = bdrv_pwrite(bs->file, tb_offset, tb, tb_size * sizeof(tb[0])); + ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", bm_name); diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 539f9ca2d50..54b2d5f4de1 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -223,8 +223,8 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); } - ret = bdrv_pwrite(bs->file, c->entries[i].offset, - qcow2_cache_get_table_addr(c, i), c->table_size); + ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->table_size, + qcow2_cache_get_table_addr(c, i), 0); if (ret < 0) { return ret; } @@ -379,9 +379,8 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); } - ret = bdrv_pread(bs->file, offset, - qcow2_cache_get_table_addr(c, i), - c->table_size); + ret = bdrv_pread(bs->file, offset, c->table_size, + qcow2_cache_get_table_addr(c, i), 0); if (ret < 0) { return ret; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 20a16ba6ee0..fd32316d6f0 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -159,8 +159,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); - ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, - new_l1_table, new_l1_size2); + ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_size2, + new_l1_table, 0); if (ret < 0) goto fail; for(i = 0; i < s->l1_size; i++) @@ -171,7 +171,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, stl_be_p(data, new_l1_size); stq_be_p(data + 4, new_l1_table_offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), - data, sizeof(data)); + sizeof(data), data, 0); if (ret < 0) { goto fail; } @@ -249,7 +249,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + L1E_SIZE * l1_start_index, - buf, bufsize); + bufsize, buf, 0); if (ret < 0) { return ret; } @@ -2260,7 +2260,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, (void **)&l2_slice); } else { /* load inactive L2 tables from disk */ - ret = bdrv_pread(bs->file, slice_offset, l2_slice, slice_size2); + ret = bdrv_pread(bs->file, slice_offset, slice_size2, + l2_slice, 0); } if (ret < 0) { goto fail; @@ -2376,8 +2377,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_pwrite(bs->file, slice_offset, - l2_slice, slice_size2); + ret = bdrv_pwrite(bs->file, slice_offset, slice_size2, + l2_slice, 0); if (ret < 0) { goto fail; } @@ -2470,8 +2471,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, l1_table = new_l1_table; - ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, - l1_table, l1_size2); + ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, l1_size2, + l1_table, 0); if (ret < 0) { goto fail; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index ed0ecfaa89a..c4d99817b68 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -119,7 +119,7 @@ int qcow2_refcount_init(BlockDriverState *bs) } BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); ret = bdrv_pread(bs->file, s->refcount_table_offset, - s->refcount_table, refcount_table_size2); + refcount_table_size2, s->refcount_table, 0); if (ret < 0) { goto fail; } @@ -439,7 +439,7 @@ static int alloc_refcount_block(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + refcount_table_index * REFTABLE_ENTRY_SIZE, - &data64, sizeof(data64)); + sizeof(data64), &data64, 0); if (ret < 0) { goto fail; } @@ -684,8 +684,8 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, } BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); - ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, - table_size * REFTABLE_ENTRY_SIZE); + ret = bdrv_pwrite_sync(bs->file, table_offset, + table_size * REFTABLE_ENTRY_SIZE, new_table, 0); if (ret < 0) { goto fail; } @@ -704,7 +704,7 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), - &data, sizeof(data)); + sizeof(data), &data, 0); if (ret < 0) { goto fail; } @@ -1274,7 +1274,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } l1_allocated = true; - ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pread(bs->file, l1_table_offset, l1_size2, l1_table, 0); if (ret < 0) { goto fail; } @@ -1435,8 +1435,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, cpu_to_be64s(&l1_table[i]); } - ret = bdrv_pwrite_sync(bs->file, l1_table_offset, - l1_table, l1_size2); + ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_size2, l1_table, + 0); for (i = 0; i < l1_size; i++) { be64_to_cpus(&l1_table[i]); @@ -1633,8 +1633,8 @@ static int fix_l2_entry_by_zero(BlockDriverState *bs, BdrvCheckResult *res, goto fail; } - ret = bdrv_pwrite_sync(bs->file, l2e_offset, &l2_table[idx], - l2_entry_size(s)); + ret = bdrv_pwrite_sync(bs->file, l2e_offset, l2_entry_size(s), + &l2_table[idx], 0); if (ret < 0) { fprintf(stderr, "ERROR: Failed to overwrite L2 " "table entry: %s\n", strerror(-ret)); @@ -1672,7 +1672,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, bool metadata_overlap; /* Read L2 table from disk */ - ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size_bytes); + ret = bdrv_pread(bs->file, l2_offset, l2_size_bytes, l2_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); res->check_errors++; @@ -1888,7 +1888,7 @@ static int check_refcounts_l1(BlockDriverState *bs, } /* Read L1 table entries from disk */ - ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size_bytes); + ret = bdrv_pread(bs->file, l1_table_offset, l1_size_bytes, l1_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); res->check_errors++; @@ -2004,8 +2004,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } } - ret = bdrv_pread(bs->file, l2_offset, l2_table, - s->l2_size * l2_entry_size(s)); + ret = bdrv_pread(bs->file, l2_offset, s->l2_size * l2_entry_size(s), + l2_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: Could not read L2 table: %s\n", strerror(-ret)); @@ -2058,8 +2058,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, goto fail; } - ret = bdrv_pwrite(bs->file, l2_offset, l2_table, - s->cluster_size); + ret = bdrv_pwrite(bs->file, l2_offset, s->cluster_size, l2_table, + 0); if (ret < 0) { fprintf(stderr, "ERROR: Could not write L2 table: %s\n", strerror(-ret)); @@ -2577,8 +2577,8 @@ static int rebuild_refcounts_write_refblocks( on_disk_refblock = (void *)((char *) *refcount_table + refblock_index * s->cluster_size); - ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock, - s->cluster_size); + ret = bdrv_pwrite(bs->file, refblock_offset, s->cluster_size, + on_disk_refblock, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR writing refblock"); return ret; @@ -2733,8 +2733,8 @@ static int rebuild_refcount_structure(BlockDriverState *bs, } assert(reftable_length < INT_MAX); - ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, - reftable_length); + ret = bdrv_pwrite(bs->file, reftable_offset, reftable_length, + on_disk_reftable, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR writing reftable"); goto fail; @@ -2746,8 +2746,8 @@ static int rebuild_refcount_structure(BlockDriverState *bs, cpu_to_be32(reftable_clusters); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), - &reftable_offset_and_clusters, - sizeof(reftable_offset_and_clusters)); + sizeof(reftable_offset_and_clusters), + &reftable_offset_and_clusters, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR setting reftable"); goto fail; @@ -3009,7 +3009,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, return -ENOMEM; } - ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2); + ret = bdrv_pread(bs->file, l1_ofs, l1_sz2, l1, 0); if (ret < 0) { g_free(l1); return ret; @@ -3180,7 +3180,7 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, return ret; } - ret = bdrv_pwrite(bs->file, offset, refblock, s->cluster_size); + ret = bdrv_pwrite(bs->file, offset, s->cluster_size, refblock, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write refblock"); return ret; @@ -3452,8 +3452,9 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, cpu_to_be64s(&new_reftable[i]); } - ret = bdrv_pwrite(bs->file, new_reftable_offset, new_reftable, - new_reftable_size * REFTABLE_ENTRY_SIZE); + ret = bdrv_pwrite(bs->file, new_reftable_offset, + new_reftable_size * REFTABLE_ENTRY_SIZE, new_reftable, + 0); for (i = 0; i < new_reftable_size; i++) { be64_to_cpus(&new_reftable[i]); @@ -3656,8 +3657,9 @@ int qcow2_shrink_reftable(BlockDriverState *bs) reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); } - ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset, reftable_tmp, - s->refcount_table_size * REFTABLE_ENTRY_SIZE); + ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset, + s->refcount_table_size * REFTABLE_ENTRY_SIZE, + reftable_tmp, 0); /* * If the write in the reftable failed the image may contain a partially * overwritten reftable. In this case it would be better to clear the diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 075269a0237..d1d46facbf2 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -108,7 +108,7 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Read statically sized part of the snapshot header */ offset = ROUND_UP(offset, 8); - ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); + ret = bdrv_pread(bs->file, offset, sizeof(h), &h, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -146,8 +146,8 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, } /* Read known extra data */ - ret = bdrv_pread(bs->file, offset, &extra, - MIN(sizeof(extra), sn->extra_data_size)); + ret = bdrv_pread(bs->file, offset, + MIN(sizeof(extra), sn->extra_data_size), &extra, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -184,8 +184,8 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Store unknown extra data */ unknown_extra_data_size = sn->extra_data_size - sizeof(extra); sn->unknown_extra_data = g_malloc(unknown_extra_data_size); - ret = bdrv_pread(bs->file, offset, sn->unknown_extra_data, - unknown_extra_data_size); + ret = bdrv_pread(bs->file, offset, unknown_extra_data_size, + sn->unknown_extra_data, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); @@ -196,7 +196,7 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); - ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); + ret = bdrv_pread(bs->file, offset, id_str_size, sn->id_str, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -206,7 +206,7 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Read snapshot name */ sn->name = g_malloc(name_size + 1); - ret = bdrv_pread(bs->file, offset, sn->name, name_size); + ret = bdrv_pread(bs->file, offset, name_size, sn->name, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -349,13 +349,13 @@ int qcow2_write_snapshots(BlockDriverState *bs) h.name_size = cpu_to_be16(name_size); offset = ROUND_UP(offset, 8); - ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); + ret = bdrv_pwrite(bs->file, offset, sizeof(h), &h, 0); if (ret < 0) { goto fail; } offset += sizeof(h); - ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); + ret = bdrv_pwrite(bs->file, offset, sizeof(extra), &extra, 0); if (ret < 0) { goto fail; } @@ -369,21 +369,21 @@ int qcow2_write_snapshots(BlockDriverState *bs) assert(unknown_extra_data_size <= BDRV_REQUEST_MAX_BYTES); assert(sn->unknown_extra_data); - ret = bdrv_pwrite(bs->file, offset, sn->unknown_extra_data, - unknown_extra_data_size); + ret = bdrv_pwrite(bs->file, offset, unknown_extra_data_size, + sn->unknown_extra_data, 0); if (ret < 0) { goto fail; } offset += unknown_extra_data_size; } - ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); + ret = bdrv_pwrite(bs->file, offset, id_str_size, sn->id_str, 0); if (ret < 0) { goto fail; } offset += id_str_size; - ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); + ret = bdrv_pwrite(bs->file, offset, name_size, sn->name, 0); if (ret < 0) { goto fail; } @@ -406,7 +406,7 @@ int qcow2_write_snapshots(BlockDriverState *bs) header_data.snapshots_offset = cpu_to_be64(snapshots_offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), - &header_data, sizeof(header_data)); + sizeof(header_data), &header_data, 0); if (ret < 0) { goto fail; } @@ -442,7 +442,8 @@ int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, /* qcow2_do_open() discards this information in check mode */ ret = bdrv_pread(bs->file, offsetof(QCowHeader, nb_snapshots), - &snapshot_table_pointer, sizeof(snapshot_table_pointer)); + sizeof(snapshot_table_pointer), &snapshot_table_pointer, + 0); if (ret < 0) { result->check_errors++; fprintf(stderr, "ERROR failed to read the snapshot table pointer from " @@ -511,9 +512,9 @@ int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, assert(fix & BDRV_FIX_ERRORS); snapshot_table_pointer.nb_snapshots = cpu_to_be32(s->nb_snapshots); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), - &snapshot_table_pointer.nb_snapshots, - sizeof(snapshot_table_pointer.nb_snapshots)); + ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), + sizeof(snapshot_table_pointer.nb_snapshots), + &snapshot_table_pointer.nb_snapshots, 0); if (ret < 0) { result->check_errors++; fprintf(stderr, "ERROR failed to update the snapshot count in the " @@ -693,8 +694,8 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto fail; } - ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, - s->l1_size * L1E_SIZE); + ret = bdrv_pwrite(bs->file, sn->l1_table_offset, s->l1_size * L1E_SIZE, + l1_table, 0); if (ret < 0) { goto fail; } @@ -829,8 +830,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pread(bs->file, sn->l1_table_offset, - sn_l1_table, sn_l1_bytes); + ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_bytes, sn_l1_table, + 0); if (ret < 0) { goto fail; } @@ -848,8 +849,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, - cur_l1_bytes); + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, cur_l1_bytes, + sn_l1_table, 0); if (ret < 0) { goto fail; } @@ -1051,8 +1052,8 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, return -ENOMEM; } - ret = bdrv_pread(bs->file, sn->l1_table_offset, - new_l1_table, new_l1_bytes); + ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_bytes, + new_l1_table, 0); if (ret < 0) { error_setg(errp, "Failed to read l1 table for snapshot"); qemu_vfree(new_l1_table); diff --git a/block/qcow2.c b/block/qcow2.c index 4f5e6440fba..c6c6692fb7f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -94,9 +94,9 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) } -static ssize_t qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, - uint8_t *buf, size_t buflen, - void *opaque, Error **errp) +static int qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, + uint8_t *buf, size_t buflen, + void *opaque, Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -107,18 +107,18 @@ static ssize_t qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, return -1; } - ret = bdrv_pread(bs->file, - s->crypto_header.offset + offset, buf, buflen); + ret = bdrv_pread(bs->file, s->crypto_header.offset + offset, buflen, buf, + 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return -1; } - return ret; + return 0; } -static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, - void *opaque, Error **errp) +static int qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, + void *opaque, Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -151,13 +151,13 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, return -1; } - return ret; + return 0; } -static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, - const uint8_t *buf, size_t buflen, - void *opaque, Error **errp) +static int qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, + const uint8_t *buf, size_t buflen, + void *opaque, Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -168,13 +168,13 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, return -1; } - ret = bdrv_pwrite(bs->file, - s->crypto_header.offset + offset, buf, buflen); + ret = bdrv_pwrite(bs->file, s->crypto_header.offset + offset, buflen, buf, + 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return -1; } - return ret; + return 0; } static QDict* @@ -227,7 +227,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("attempting to read extended header in offset %lu\n", offset); #endif - ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); + ret = bdrv_pread(bs->file, offset, sizeof(ext), &ext, 0); if (ret < 0) { error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " "pread fail from offset %" PRIu64, offset); @@ -255,7 +255,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, sizeof(bs->backing_format)); return 2; } - ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); + ret = bdrv_pread(bs->file, offset, ext.len, bs->backing_format, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " "Could not read format name"); @@ -271,7 +271,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); - ret = bdrv_pread(bs->file, offset , feature_table, ext.len); + ret = bdrv_pread(bs->file, offset, ext.len, feature_table, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " "Could not read table"); @@ -296,7 +296,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return -EINVAL; } - ret = bdrv_pread(bs->file, offset, &s->crypto_header, ext.len); + ret = bdrv_pread(bs->file, offset, ext.len, &s->crypto_header, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Unable to read CRYPTO header extension"); @@ -352,7 +352,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, break; } - ret = bdrv_pread(bs->file, offset, &bitmaps_ext, ext.len); + ret = bdrv_pread(bs->file, offset, ext.len, &bitmaps_ext, 0); if (ret < 0) { error_setg_errno(errp, -ret, "bitmaps_ext: " "Could not read ext header"); @@ -416,7 +416,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_DATA_FILE: { s->image_data_file = g_malloc0(ext.len + 1); - ret = bdrv_pread(bs->file, offset, s->image_data_file, ext.len); + ret = bdrv_pread(bs->file, offset, ext.len, s->image_data_file, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: Could not read data file name"); @@ -440,7 +440,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uext->len = ext.len; QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); - ret = bdrv_pread(bs->file, offset , uext->data, uext->len); + ret = bdrv_pread(bs->file, offset, uext->len, uext->data, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: unknown extension: " "Could not read data"); @@ -516,12 +516,9 @@ int qcow2_mark_dirty(BlockDriverState *bs) } val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); - ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features), - &val, sizeof(val)); - if (ret < 0) { - return ret; - } - ret = bdrv_flush(bs->file->bs); + ret = bdrv_pwrite_sync(bs->file, + offsetof(QCowHeader, incompatible_features), + sizeof(val), &val, 0); if (ret < 0) { return ret; } @@ -1308,7 +1305,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, uint64_t l1_vm_state_index; bool update_header = false; - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; @@ -1384,8 +1381,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (header.header_length > sizeof(header)) { s->unknown_header_fields_size = header.header_length - sizeof(header); s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); - ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, - s->unknown_header_fields_size); + ret = bdrv_pread(bs->file, sizeof(header), + s->unknown_header_fields_size, + s->unknown_header_fields, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " "fields"); @@ -1580,8 +1578,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ret = -ENOMEM; goto fail; } - ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, - s->l1_size * L1E_SIZE); + ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_size * L1E_SIZE, + s->l1_table, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read L1 table"); goto fail; @@ -1698,8 +1696,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file, header.backing_file_offset, - bs->auto_backing_file, len); + ret = bdrv_pread(bs->file, header.backing_file_offset, len, + bs->auto_backing_file, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read backing file name"); goto fail; @@ -3081,7 +3079,7 @@ int qcow2_update_header(BlockDriverState *bs) } /* Write the new header */ - ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size); + ret = bdrv_pwrite(bs->file, 0, s->cluster_size, header, 0); if (ret < 0) { goto fail; } @@ -3668,7 +3666,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) cpu_to_be64(QCOW2_INCOMPAT_EXTL2); } - ret = blk_pwrite(blk, 0, header, cluster_size, 0); + ret = blk_pwrite(blk, 0, cluster_size, header, 0); g_free(header); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write qcow2 header"); @@ -3678,7 +3676,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) /* Write a refcount table with one refcount block */ refcount_table = g_malloc0(2 * cluster_size); refcount_table[0] = cpu_to_be64(2 * cluster_size); - ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size, 0); + ret = blk_pwrite(blk, cluster_size, 2 * cluster_size, refcount_table, 0); g_free(refcount_table); if (ret < 0) { @@ -4550,8 +4548,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, /* write updated header.size */ offset = cpu_to_be64(offset); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), - &offset, sizeof(offset)); + ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, size), + sizeof(offset), &offset, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to update the image size"); goto fail; @@ -4828,7 +4826,7 @@ static int make_completely_empty(BlockDriverState *bs) l1_ofs_rt_ofs_cls.reftable_offset = cpu_to_be64(s->cluster_size); l1_ofs_rt_ofs_cls.reftable_clusters = cpu_to_be32(1); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), - &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls)); + sizeof(l1_ofs_rt_ofs_cls), &l1_ofs_rt_ofs_cls, 0); if (ret < 0) { goto fail_broken_refcounts; } @@ -4859,8 +4857,8 @@ static int make_completely_empty(BlockDriverState *bs) /* Enter the first refblock into the reftable */ rt_entry = cpu_to_be64(2 * s->cluster_size); - ret = bdrv_pwrite_sync(bs->file, s->cluster_size, - &rt_entry, sizeof(rt_entry)); + ret = bdrv_pwrite_sync(bs->file, s->cluster_size, sizeof(rt_entry), + &rt_entry, 0); if (ret < 0) { goto fail_broken_refcounts; } diff --git a/block/qed.c b/block/qed.c index f34d9a3ac1a..40943e679b4 100644 --- a/block/qed.c +++ b/block/qed.c @@ -87,14 +87,9 @@ static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le) int qed_write_header_sync(BDRVQEDState *s) { QEDHeader le; - int ret; qed_header_cpu_to_le(&s->header, &le); - ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); - if (ret != sizeof(le)) { - return ret; - } - return 0; + return bdrv_pwrite(s->bs->file, 0, sizeof(le), &le, 0); } /** @@ -207,7 +202,7 @@ static int qed_read_string(BdrvChild *file, uint64_t offset, size_t n, if (n >= buflen) { return -EINVAL; } - ret = bdrv_pread(file, offset, buf, n); + ret = bdrv_pread(file, offset, n, buf, 0); if (ret < 0) { return ret; } @@ -392,7 +387,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int64_t file_size; int ret; - ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); + ret = bdrv_pread(bs->file, 0, sizeof(le_header), &le_header, 0); if (ret < 0) { error_setg(errp, "Failed to read QED header"); return ret; @@ -710,18 +705,18 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, } qed_header_cpu_to_le(&header, &le_header); - ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0); + ret = blk_pwrite(blk, 0, sizeof(le_header), &le_header, 0); if (ret < 0) { goto out; } - ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file, - header.backing_filename_size, 0); + ret = blk_pwrite(blk, sizeof(le_header), header.backing_filename_size, + qed_opts->backing_file, 0); if (ret < 0) { goto out; } l1_table = g_malloc0(l1_size); - ret = blk_pwrite(blk, header.l1_table_offset, l1_table, l1_size, 0); + ret = blk_pwrite(blk, header.l1_table_offset, l1_size, l1_table, 0); if (ret < 0) { goto out; } @@ -1545,7 +1540,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, } /* Write new header */ - ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); + ret = bdrv_pwrite_sync(bs->file, 0, buffer_len, buffer, 0); g_free(buffer); if (ret == 0) { memcpy(&s->header, &new_header, sizeof(new_header)); diff --git a/block/rbd.c b/block/rbd.c index 6caf35cbbad..f826410f40f 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -831,6 +831,26 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, error_setg_errno(errp, -r, "error opening pool %s", opts->pool); goto failed_shutdown; } + +#ifdef HAVE_RBD_NAMESPACE_EXISTS + if (opts->has_q_namespace && strlen(opts->q_namespace) > 0) { + bool exists; + + r = rbd_namespace_exists(*io_ctx, opts->q_namespace, &exists); + if (r < 0) { + error_setg_errno(errp, -r, "error checking namespace"); + goto failed_ioctx_destroy; + } + + if (!exists) { + error_setg(errp, "namespace '%s' does not exist", + opts->q_namespace); + r = -ENOENT; + goto failed_ioctx_destroy; + } + } +#endif + /* * Set the namespace after opening the io context on the pool, * if nspace == NULL or if nspace == "", it is just as we did nothing @@ -840,6 +860,10 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, r = 0; goto out; +#ifdef HAVE_RBD_NAMESPACE_EXISTS +failed_ioctx_destroy: + rados_ioctx_destroy(*io_ctx); +#endif failed_shutdown: rados_shutdown(*cluster); out: diff --git a/block/trace-events b/block/trace-events index 549090d453e..48dbf10c66f 100644 --- a/block/trace-events +++ b/block/trace-events @@ -172,6 +172,8 @@ 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_client_handshake(const char *export_name) "export '%s'" nbd_client_handshake_success(const char *export_name) "export '%s'" +nbd_reconnect_attempt(unsigned in_flight) "in_flight %u" +nbd_reconnect_attempt_result(int ret, unsigned in_flight) "ret %d in_flight %u" # ssh.c ssh_restart_coroutine(void *co) "co=%p" diff --git a/block/vdi.c b/block/vdi.c index cca3a3a3567..e942325455d 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -385,7 +385,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, logout("\n"); - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { goto fail; } @@ -485,8 +485,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, header.offset_bmap, s->bmap, - bmap_size * SECTOR_SIZE); + ret = bdrv_pread(bs->file, header.offset_bmap, bmap_size * SECTOR_SIZE, + s->bmap, 0); if (ret < 0) { goto fail_free_bmap; } @@ -664,7 +664,7 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, * so this full-cluster write does not overlap a partial write * of the same cluster, issued from the "else" branch. */ - ret = bdrv_pwrite(bs->file, data_offset, block, s->block_size); + ret = bdrv_pwrite(bs->file, data_offset, s->block_size, block, 0); qemu_co_rwlock_unlock(&s->bmap_lock); } else { nonallocating_write: @@ -709,7 +709,7 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, assert(VDI_IS_ALLOCATED(bmap_first)); *header = s->header; vdi_header_to_le(header); - ret = bdrv_pwrite(bs->file, 0, header, sizeof(*header)); + ret = bdrv_pwrite(bs->file, 0, sizeof(*header), header, 0); g_free(header); if (ret < 0) { @@ -726,11 +726,11 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; logout("will write %u block map sectors starting from entry %u\n", n_sectors, bmap_first); - ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE, base, - n_sectors * SECTOR_SIZE); + ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE, + n_sectors * SECTOR_SIZE, base, 0); } - return ret < 0 ? ret : 0; + return ret; } static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, @@ -845,7 +845,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, vdi_header_print(&header); } vdi_header_to_le(&header); - ret = blk_pwrite(blk, offset, &header, sizeof(header), 0); + ret = blk_pwrite(blk, offset, sizeof(header), &header, 0); if (ret < 0) { error_setg(errp, "Error writing header"); goto exit; @@ -866,7 +866,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, bmap[i] = VDI_UNALLOCATED; } } - ret = blk_pwrite(blk, offset, bmap, bmap_size, 0); + ret = blk_pwrite(blk, offset, bmap_size, bmap, 0); if (ret < 0) { error_setg(errp, "Error writing bmap"); goto exit; diff --git a/block/vhdx-log.c b/block/vhdx-log.c index ff0d4e0da05..572582b87bf 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -84,7 +84,7 @@ static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, offset = log->offset + read; - ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader)); + ret = bdrv_pread(bs->file, offset, sizeof(VHDXLogEntryHeader), hdr, 0); if (ret < 0) { goto exit; } @@ -144,7 +144,7 @@ static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, } offset = log->offset + read; - ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pread(bs->file, offset, VHDX_LOG_SECTOR_SIZE, buffer, 0); if (ret < 0) { goto exit; } @@ -194,8 +194,8 @@ static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, /* full */ break; } - ret = bdrv_pwrite(bs->file, offset, buffer_tmp, - VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pwrite(bs->file, offset, VHDX_LOG_SECTOR_SIZE, buffer_tmp, + 0); if (ret < 0) { goto exit; } @@ -466,8 +466,8 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, /* count is only > 1 if we are writing zeroes */ for (i = 0; i < count; i++) { - ret = bdrv_pwrite_sync(bs->file, file_offset, buffer, - VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pwrite_sync(bs->file, file_offset, VHDX_LOG_SECTOR_SIZE, + buffer, 0); if (ret < 0) { goto exit; } @@ -970,8 +970,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, if (i == 0 && leading_length) { /* partial sector at the front of the buffer */ - ret = bdrv_pread(bs->file, file_offset, merged_sector, - VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pread(bs->file, file_offset, VHDX_LOG_SECTOR_SIZE, + merged_sector, 0); if (ret < 0) { goto exit; } @@ -980,10 +980,9 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, sector_write = merged_sector; } else if (i == sectors - 1 && trailing_length) { /* partial sector at the end of the buffer */ - ret = bdrv_pread(bs->file, - file_offset, - merged_sector + trailing_length, - VHDX_LOG_SECTOR_SIZE - trailing_length); + ret = bdrv_pread(bs->file, file_offset, + VHDX_LOG_SECTOR_SIZE - trailing_length, + merged_sector + trailing_length, 0); if (ret < 0) { goto exit; } diff --git a/block/vhdx.c b/block/vhdx.c index 410c6f96101..e10e78ebfdf 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -326,7 +326,7 @@ static int vhdx_write_header(BdrvChild *file, VHDXHeader *hdr, buffer = qemu_blockalign(bs_file, VHDX_HEADER_SIZE); if (read) { /* if true, we can't assume the extra reserved bytes are 0 */ - ret = bdrv_pread(file, offset, buffer, VHDX_HEADER_SIZE); + ret = bdrv_pread(file, offset, VHDX_HEADER_SIZE, buffer, 0); if (ret < 0) { goto exit; } @@ -340,7 +340,7 @@ static int vhdx_write_header(BdrvChild *file, VHDXHeader *hdr, vhdx_header_le_export(hdr, header_le); vhdx_update_checksum(buffer, VHDX_HEADER_SIZE, offsetof(VHDXHeader, checksum)); - ret = bdrv_pwrite_sync(file, offset, header_le, sizeof(VHDXHeader)); + ret = bdrv_pwrite_sync(file, offset, sizeof(VHDXHeader), header_le, 0); exit: qemu_vfree(buffer); @@ -440,8 +440,8 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, /* We have to read the whole VHDX_HEADER_SIZE instead of * sizeof(VHDXHeader), because the checksum is over the whole * region */ - ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, - VHDX_HEADER_SIZE); + ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, VHDX_HEADER_SIZE, buffer, + 0); if (ret < 0) { goto fail; } @@ -457,8 +457,8 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, } } - ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, - VHDX_HEADER_SIZE); + ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, VHDX_HEADER_SIZE, buffer, + 0); if (ret < 0) { goto fail; } @@ -531,8 +531,8 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) * whole block */ buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE); - ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE); + ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, + VHDX_HEADER_BLOCK_SIZE, buffer, 0); if (ret < 0) { goto fail; } @@ -644,8 +644,8 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE); - ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer, - VHDX_METADATA_TABLE_MAX_SIZE); + ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, + VHDX_METADATA_TABLE_MAX_SIZE, buffer, 0); if (ret < 0) { goto exit; } @@ -750,8 +750,9 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) ret = bdrv_pread(bs->file, s->metadata_entries.file_parameters_entry.offset + s->metadata_rt.file_offset, + sizeof(s->params), &s->params, - sizeof(s->params)); + 0); if (ret < 0) { goto exit; @@ -785,24 +786,27 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) ret = bdrv_pread(bs->file, s->metadata_entries.virtual_disk_size_entry.offset + s->metadata_rt.file_offset, + sizeof(uint64_t), &s->virtual_disk_size, - sizeof(uint64_t)); + 0); if (ret < 0) { goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.logical_sector_size_entry.offset + s->metadata_rt.file_offset, + sizeof(uint32_t), &s->logical_sector_size, - sizeof(uint32_t)); + 0); if (ret < 0) { goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.phys_sector_size_entry.offset + s->metadata_rt.file_offset, + sizeof(uint32_t), &s->physical_sector_size, - sizeof(uint32_t)); + 0); if (ret < 0) { goto exit; } @@ -1010,7 +1014,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, QLIST_INIT(&s->regions); /* validate the file signature */ - ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); + ret = bdrv_pread(bs->file, 0, sizeof(uint64_t), &signature, 0); if (ret < 0) { goto fail; } @@ -1069,7 +1073,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); + ret = bdrv_pread(bs->file, s->bat_offset, s->bat_rt.length, s->bat, 0); if (ret < 0) { goto fail; } @@ -1661,13 +1665,13 @@ static int vhdx_create_new_metadata(BlockBackend *blk, VHDX_META_FLAGS_IS_VIRTUAL_DISK; vhdx_metadata_entry_le_export(&md_table_entry[4]); - ret = blk_pwrite(blk, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE, 0); + ret = blk_pwrite(blk, metadata_offset, VHDX_HEADER_BLOCK_SIZE, buffer, 0); if (ret < 0) { goto exit; } - ret = blk_pwrite(blk, metadata_offset + (64 * KiB), entry_buffer, - VHDX_METADATA_ENTRY_BUFFER_SIZE, 0); + ret = blk_pwrite(blk, metadata_offset + (64 * KiB), + VHDX_METADATA_ENTRY_BUFFER_SIZE, entry_buffer, 0); if (ret < 0) { goto exit; } @@ -1752,7 +1756,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, s->bat[sinfo.bat_idx] = cpu_to_le64(s->bat[sinfo.bat_idx]); sector_num += s->sectors_per_block; } - ret = blk_pwrite(blk, file_offset, s->bat, length, 0); + ret = blk_pwrite(blk, file_offset, length, s->bat, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write the BAT"); goto exit; @@ -1856,15 +1860,15 @@ static int vhdx_create_new_region_table(BlockBackend *blk, } /* Now write out the region headers to disk */ - ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE, 0); + ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, VHDX_HEADER_BLOCK_SIZE, + buffer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write first region table"); goto exit; } - ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE, 0); + ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, VHDX_HEADER_BLOCK_SIZE, + buffer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write second region table"); goto exit; @@ -2008,7 +2012,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL, &creator_items, NULL); signature = cpu_to_le64(VHDX_FILE_SIGNATURE); - ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature), + ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, sizeof(signature), &signature, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write file signature"); @@ -2016,7 +2020,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, } if (creator) { ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature), - creator, creator_items * sizeof(gunichar2), 0); + creator_items * sizeof(gunichar2), creator, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write creator field"); goto delete_and_exit; diff --git a/block/vmdk.c b/block/vmdk.c index 38e5ab38069..fe07a54866d 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -307,7 +307,7 @@ static int vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid) int ret; desc = g_malloc0(DESC_SIZE); - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, DESC_SIZE, desc, 0); if (ret < 0) { goto out; } @@ -348,7 +348,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) desc = g_malloc0(DESC_SIZE); tmp_desc = g_malloc0(DESC_SIZE); - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, DESC_SIZE, desc, 0); if (ret < 0) { goto out; } @@ -368,7 +368,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) pstrcat(desc, DESC_SIZE, tmp_desc); } - ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pwrite_sync(bs->file, s->desc_offset, DESC_SIZE, desc, 0); out: g_free(desc); @@ -469,11 +469,10 @@ static int vmdk_parent_open(BlockDriverState *bs) int ret; desc = g_malloc0(DESC_SIZE + 1); - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, DESC_SIZE, desc, 0); if (ret < 0) { goto out; } - ret = 0; p_name = strstr(desc, "parentFileNameHint"); if (p_name != NULL) { @@ -589,10 +588,8 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, return -ENOMEM; } - ret = bdrv_pread(extent->file, - extent->l1_table_offset, - extent->l1_table, - l1_size); + ret = bdrv_pread(extent->file, extent->l1_table_offset, l1_size, + extent->l1_table, 0); if (ret < 0) { bdrv_refresh_filename(extent->file->bs); error_setg_errno(errp, -ret, @@ -616,10 +613,8 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, ret = -ENOMEM; goto fail_l1; } - ret = bdrv_pread(extent->file, - extent->l1_backup_table_offset, - extent->l1_backup_table, - l1_size); + ret = bdrv_pread(extent->file, extent->l1_backup_table_offset, + l1_size, extent->l1_backup_table, 0); if (ret < 0) { bdrv_refresh_filename(extent->file->bs); error_setg_errno(errp, -ret, @@ -651,7 +646,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, VMDK3Header header; VmdkExtent *extent = NULL; - ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -815,7 +810,7 @@ static int vmdk_open_se_sparse(BlockDriverState *bs, assert(sizeof(const_header) == SECTOR_SIZE); - ret = bdrv_pread(file, 0, &const_header, sizeof(const_header)); + ret = bdrv_pread(file, 0, sizeof(const_header), &const_header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -832,9 +827,8 @@ static int vmdk_open_se_sparse(BlockDriverState *bs, assert(sizeof(volatile_header) == SECTOR_SIZE); - ret = bdrv_pread(file, - const_header.volatile_header_offset * SECTOR_SIZE, - &volatile_header, sizeof(volatile_header)); + ret = bdrv_pread(file, const_header.volatile_header_offset * SECTOR_SIZE, + sizeof(volatile_header), &volatile_header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -904,13 +898,13 @@ static char *vmdk_read_desc(BdrvChild *file, uint64_t desc_offset, Error **errp) size = MIN(size, (1 << 20) - 1); /* avoid unbounded allocation */ buf = g_malloc(size + 1); - ret = bdrv_pread(file, desc_offset, buf, size); + ret = bdrv_pread(file, desc_offset, size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read from file"); g_free(buf); return NULL; } - buf[ret] = 0; + buf[size] = 0; return buf; } @@ -928,7 +922,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, int64_t l1_backup_offset = 0; bool compressed; - ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -979,9 +973,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } QEMU_PACKED eos_marker; } QEMU_PACKED footer; - ret = bdrv_pread(file, - bs->file->bs->total_sectors * 512 - 1536, - &footer, sizeof(footer)); + ret = bdrv_pread(file, bs->file->bs->total_sectors * 512 - 1536, + sizeof(footer), &footer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read footer"); return ret; @@ -1448,16 +1441,16 @@ static int get_whole_cluster(BlockDriverState *bs, if (copy_from_backing) { /* qcow2 emits this on bs->file instead of bs->backing */ BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); - ret = bdrv_pread(bs->backing, offset, whole_grain, - skip_start_bytes); + ret = bdrv_pread(bs->backing, offset, skip_start_bytes, + whole_grain, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; } } BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE); - ret = bdrv_pwrite(extent->file, cluster_offset, whole_grain, - skip_start_bytes); + ret = bdrv_pwrite(extent->file, cluster_offset, skip_start_bytes, + whole_grain, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; @@ -1469,8 +1462,8 @@ static int get_whole_cluster(BlockDriverState *bs, /* qcow2 emits this on bs->file instead of bs->backing */ BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); ret = bdrv_pread(bs->backing, offset + skip_end_bytes, - whole_grain + skip_end_bytes, - cluster_bytes - skip_end_bytes); + cluster_bytes - skip_end_bytes, + whole_grain + skip_end_bytes, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; @@ -1478,8 +1471,8 @@ static int get_whole_cluster(BlockDriverState *bs, } BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE); ret = bdrv_pwrite(extent->file, cluster_offset + skip_end_bytes, - whole_grain + skip_end_bytes, - cluster_bytes - skip_end_bytes); + cluster_bytes - skip_end_bytes, + whole_grain + skip_end_bytes, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; @@ -1501,7 +1494,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, if (bdrv_pwrite(extent->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(offset)), - &offset, sizeof(offset)) < 0) { + sizeof(offset), &offset, 0) < 0) { return VMDK_ERROR; } /* update backup L2 table */ @@ -1510,7 +1503,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, if (bdrv_pwrite(extent->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(offset)), - &offset, sizeof(offset)) < 0) { + sizeof(offset), &offset, 0) < 0) { return VMDK_ERROR; } } @@ -1633,9 +1626,10 @@ static int get_cluster_offset(BlockDriverState *bs, BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD); if (bdrv_pread(extent->file, (int64_t)l2_offset * 512, + l2_size_bytes, l2_table, - l2_size_bytes - ) != l2_size_bytes) { + 0 + ) < 0) { return VMDK_ERROR; } @@ -1903,9 +1897,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, cluster_buf = g_malloc(buf_bytes); uncomp_buf = g_malloc(cluster_bytes); BLKDBG_EVENT(extent->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_pread(extent->file, - cluster_offset, - cluster_buf, buf_bytes); + ret = bdrv_pread(extent->file, cluster_offset, buf_bytes, cluster_buf, 0); if (ret < 0) { goto out; } @@ -2244,12 +2236,12 @@ static int vmdk_init_extent(BlockBackend *blk, header.check_bytes[3] = 0xa; /* write all the data */ - ret = blk_pwrite(blk, 0, &magic, sizeof(magic), 0); + ret = blk_pwrite(blk, 0, sizeof(magic), &magic, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; } - ret = blk_pwrite(blk, sizeof(magic), &header, sizeof(header), 0); + ret = blk_pwrite(blk, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; @@ -2269,7 +2261,7 @@ static int vmdk_init_extent(BlockBackend *blk, gd_buf[i] = cpu_to_le32(tmp); } ret = blk_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, - gd_buf, gd_buf_size, 0); + gd_buf_size, gd_buf, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; @@ -2281,7 +2273,7 @@ static int vmdk_init_extent(BlockBackend *blk, gd_buf[i] = cpu_to_le32(tmp); } ret = blk_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, - gd_buf, gd_buf_size, 0); + gd_buf_size, gd_buf, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); } @@ -2592,7 +2584,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, desc_offset = 0x200; } - ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0); + ret = blk_pwrite(blk, desc_offset, desc_len, desc, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write description"); goto exit; diff --git a/block/vpc.c b/block/vpc.c index 4d8f16e1990..4f49ef207fe 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -252,7 +252,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, 0, &s->footer, sizeof(s->footer)); + ret = bdrv_pread(bs->file, 0, sizeof(s->footer), &s->footer, 0); if (ret < 0) { error_setg(errp, "Unable to read VHD header"); goto fail; @@ -272,8 +272,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } /* If a fixed disk, the footer is found only at the end of the file */ - ret = bdrv_pread(bs->file, offset - sizeof(*footer), - footer, sizeof(*footer)); + ret = bdrv_pread(bs->file, offset - sizeof(*footer), sizeof(*footer), + footer, 0); if (ret < 0) { goto fail; } @@ -347,7 +347,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, if (disk_type == VHD_DYNAMIC) { ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), - &dyndisk_header, sizeof(dyndisk_header)); + sizeof(dyndisk_header), &dyndisk_header, 0); if (ret < 0) { error_setg(errp, "Error reading dynamic VHD header"); goto fail; @@ -401,8 +401,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, s->bat_offset = be64_to_cpu(dyndisk_header.table_offset); - ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, - pagetable_size); + ret = bdrv_pread(bs->file, s->bat_offset, pagetable_size, + s->pagetable, 0); if (ret < 0) { error_setg(errp, "Error reading pagetable"); goto fail; @@ -516,7 +516,8 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, s->last_bitmap_offset = bitmap_offset; memset(bitmap, 0xff, s->bitmap_size); - r = bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); + r = bdrv_pwrite_sync(bs->file, bitmap_offset, s->bitmap_size, bitmap, + 0); if (r < 0) { *err = r; return -2; @@ -538,7 +539,7 @@ static int rewrite_footer(BlockDriverState *bs) BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(bs->file, offset, &s->footer, sizeof(s->footer)); + ret = bdrv_pwrite_sync(bs->file, offset, sizeof(s->footer), &s->footer, 0); if (ret < 0) return ret; @@ -572,8 +573,8 @@ static int64_t alloc_block(BlockDriverState *bs, int64_t offset) /* Initialize the block's bitmap */ memset(bitmap, 0xff, s->bitmap_size); - ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, - s->bitmap_size); + ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, + s->bitmap_size, bitmap, 0); if (ret < 0) { return ret; } @@ -587,7 +588,7 @@ static int64_t alloc_block(BlockDriverState *bs, int64_t offset) /* Write BAT entry to disk */ bat_offset = s->bat_offset + (4 * index); bat_value = cpu_to_be32(s->pagetable[index]); - ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); + ret = bdrv_pwrite_sync(bs->file, bat_offset, 4, &bat_value, 0); if (ret < 0) goto fail; @@ -833,13 +834,13 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, block_size = 0x200000; num_bat_entries = DIV_ROUND_UP(total_sectors, block_size / 512); - ret = blk_pwrite(blk, offset, footer, sizeof(*footer), 0); + ret = blk_pwrite(blk, offset, sizeof(*footer), footer, 0); if (ret < 0) { goto fail; } offset = 1536 + ((num_bat_entries * 4 + 511) & ~511); - ret = blk_pwrite(blk, offset, footer, sizeof(*footer), 0); + ret = blk_pwrite(blk, offset, sizeof(*footer), footer, 0); if (ret < 0) { goto fail; } @@ -849,7 +850,7 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, memset(bat_sector, 0xFF, 512); for (i = 0; i < DIV_ROUND_UP(num_bat_entries * 4, 512); i++) { - ret = blk_pwrite(blk, offset, bat_sector, 512, 0); + ret = blk_pwrite(blk, offset, 512, bat_sector, 0); if (ret < 0) { goto fail; } @@ -877,7 +878,7 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, /* Write the header */ offset = 512; - ret = blk_pwrite(blk, offset, &dyndisk_header, sizeof(dyndisk_header), 0); + ret = blk_pwrite(blk, offset, sizeof(dyndisk_header), &dyndisk_header, 0); if (ret < 0) { goto fail; } @@ -900,8 +901,8 @@ static int create_fixed_disk(BlockBackend *blk, VHDFooter *footer, return ret; } - ret = blk_pwrite(blk, total_size - sizeof(*footer), - footer, sizeof(*footer), 0); + ret = blk_pwrite(blk, total_size - sizeof(*footer), sizeof(*footer), + footer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Unable to write VHD header"); return ret; diff --git a/block/vvfat.c b/block/vvfat.c index b2b58d93b8b..d6dd919683d 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1488,8 +1488,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64 " allocated\n", sector_num, n >> BDRV_SECTOR_BITS)); - if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, - buf + i * 0x200, n) < 0) { + if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, n, + buf + i * 0x200, 0) < 0) { return -1; } i += (n >> BDRV_SECTOR_BITS) - 1; @@ -1978,7 +1978,8 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, return -1; } res = bdrv_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE, - s->cluster_buffer, BDRV_SECTOR_SIZE); + BDRV_SECTOR_SIZE, s->cluster_buffer, + 0); if (res < 0) { return -2; } @@ -3062,8 +3063,8 @@ DLOG(checkpoint()); * Use qcow backend. Commit later. */ DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); - ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, buf, - nb_sectors * BDRV_SECTOR_SIZE); + ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, buf, 0); if (ret < 0) { fprintf(stderr, "Error writing to qcow backend\n"); return ret; diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h index e9e2c85eb67..588e0c50d45 100644 --- a/bsd-user/bsd-file.h +++ b/bsd-user/bsd-file.h @@ -22,11 +22,43 @@ #include "qemu/path.h" +#define LOCK_PATH(p, arg) \ +do { \ + (p) = lock_user_string(arg); \ + if ((p) == NULL) { \ + return -TARGET_EFAULT; \ + } \ +} while (0) + +#define UNLOCK_PATH(p, arg) unlock_user(p, arg, 0) + +#define LOCK_PATH2(p1, arg1, p2, arg2) \ +do { \ + (p1) = lock_user_string(arg1); \ + if ((p1) == NULL) { \ + return -TARGET_EFAULT; \ + } \ + (p2) = lock_user_string(arg2); \ + if ((p2) == NULL) { \ + unlock_user(p1, arg1, 0); \ + return -TARGET_EFAULT; \ + } \ +} while (0) + +#define UNLOCK_PATH2(p1, arg1, p2, arg2) \ +do { \ + unlock_user(p2, arg2, 0); \ + unlock_user(p1, arg1, 0); \ +} while (0) + extern struct iovec *lock_iovec(int type, abi_ulong target_addr, int count, int copy); extern void unlock_iovec(struct iovec *vec, abi_ulong target_addr, int count, int copy); +int safe_open(const char *path, int flags, mode_t mode); +int safe_openat(int fd, const char *path, int flags, mode_t mode); + ssize_t safe_read(int fd, void *buf, size_t nbytes); ssize_t safe_pread(int fd, void *buf, size_t nbytes, off_t offset); ssize_t safe_readv(int fd, const struct iovec *iov, int iovcnt); @@ -190,4 +222,721 @@ static abi_long do_bsd_pwritev(void *cpu_env, abi_long arg1, return ret; } +/* open(2) */ +static abi_long do_bsd_open(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(safe_open(path(p), target_to_host_bitmask(arg2, + fcntl_flags_tbl), arg3)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* openat(2) */ +static abi_long do_bsd_openat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(safe_openat(arg1, path(p), + target_to_host_bitmask(arg3, fcntl_flags_tbl), arg4)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* close(2) */ +static abi_long do_bsd_close(abi_long arg1) +{ + return get_errno(close(arg1)); +} + +/* fdatasync(2) */ +static abi_long do_bsd_fdatasync(abi_long arg1) +{ + return get_errno(fdatasync(arg1)); +} + +/* fsync(2) */ +static abi_long do_bsd_fsync(abi_long arg1) +{ + return get_errno(fsync(arg1)); +} + +/* closefrom(2) */ +static abi_long do_bsd_closefrom(abi_long arg1) +{ + closefrom(arg1); /* returns void */ + return get_errno(0); +} + +/* revoke(2) */ +static abi_long do_bsd_revoke(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(revoke(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* access(2) */ +static abi_long do_bsd_access(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(access(path(p), arg2)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* eaccess(2) */ +static abi_long do_bsd_eaccess(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(eaccess(path(p), arg2)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* faccessat(2) */ +static abi_long do_bsd_faccessat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(faccessat(arg1, p, arg3, arg4)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* chdir(2) */ +static abi_long do_bsd_chdir(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chdir(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchdir(2) */ +static abi_long do_bsd_fchdir(abi_long arg1) +{ + return get_errno(fchdir(arg1)); +} + +/* rename(2) */ +static abi_long do_bsd_rename(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + ret = get_errno(rename(p1, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* renameat(2) */ +static abi_long do_bsd_renameat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg2, p2, arg4); + ret = get_errno(renameat(arg1, p1, arg3, p2)); + UNLOCK_PATH2(p1, arg2, p2, arg4); + + return ret; +} + +/* link(2) */ +static abi_long do_bsd_link(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + ret = get_errno(link(p1, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* linkat(2) */ +static abi_long do_bsd_linkat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg2, p2, arg4); + ret = get_errno(linkat(arg1, p1, arg3, p2, arg5)); + UNLOCK_PATH2(p1, arg2, p2, arg4); + + return ret; +} + +/* unlink(2) */ +static abi_long do_bsd_unlink(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(unlink(p)); /* XXX path(p) */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* unlinkat(2) */ +static abi_long do_bsd_unlinkat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(unlinkat(arg1, p, arg3)); /* XXX path(p) */ + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* mkdir(2) */ +static abi_long do_bsd_mkdir(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(mkdir(p, arg2)); /* XXX path(p) */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* mkdirat(2) */ +static abi_long do_bsd_mkdirat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(mkdirat(arg1, p, arg3)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* rmdir(2) */ +static abi_long do_bsd_rmdir(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(rmdir(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* undocumented __getcwd(char *buf, size_t len) system call */ +static abi_long do_bsd___getcwd(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_WRITE, arg1, arg2, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = safe_syscall(SYS___getcwd, p, arg2); + unlock_user(p, arg1, ret == 0 ? strlen(p) + 1 : 0); + + return get_errno(ret); +} + +/* dup(2) */ +static abi_long do_bsd_dup(abi_long arg1) +{ + return get_errno(dup(arg1)); +} + +/* dup2(2) */ +static abi_long do_bsd_dup2(abi_long arg1, abi_long arg2) +{ + return get_errno(dup2(arg1, arg2)); +} + +/* truncate(2) */ +static abi_long do_bsd_truncate(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + if (regpairs_aligned(cpu_env) != 0) { + arg2 = arg3; + arg3 = arg4; + } + ret = get_errno(truncate(p, target_arg64(arg2, arg3))); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* ftruncate(2) */ +static abi_long do_bsd_ftruncate(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4) +{ + if (regpairs_aligned(cpu_env) != 0) { + arg2 = arg3; + arg3 = arg4; + } + return get_errno(ftruncate(arg1, target_arg64(arg2, arg3))); +} + +/* acct(2) */ +static abi_long do_bsd_acct(abi_long arg1) +{ + abi_long ret; + void *p; + + if (arg1 == 0) { + ret = get_errno(acct(NULL)); + } else { + LOCK_PATH(p, arg1); + ret = get_errno(acct(path(p))); + UNLOCK_PATH(p, arg1); + } + return ret; +} + +/* sync(2) */ +static abi_long do_bsd_sync(void) +{ + sync(); + return 0; +} + +/* mount(2) */ +static abi_long do_bsd_mount(abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + /* + * XXX arg4 should be locked, but it isn't clear how to do that since it may + * be not be a NULL-terminated string. + */ + if (arg4 == 0) { + ret = get_errno(mount(p1, p2, arg3, NULL)); /* XXX path(p2)? */ + } else { + ret = get_errno(mount(p1, p2, arg3, g2h_untagged(arg4))); /* XXX path(p2)? */ + } + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* unmount(2) */ +static abi_long do_bsd_unmount(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(unmount(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* nmount(2) */ +static abi_long do_bsd_nmount(abi_long arg1, abi_long count, + abi_long flags) +{ + abi_long ret; + struct iovec *vec = lock_iovec(VERIFY_READ, arg1, count, 1); + + if (vec != NULL) { + ret = get_errno(nmount(vec, count, flags)); + unlock_iovec(vec, arg1, count, 0); + } else { + return -TARGET_EFAULT; + } + + return ret; +} + +/* symlink(2) */ +static abi_long do_bsd_symlink(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + ret = get_errno(symlink(p1, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* symlinkat(2) */ +static abi_long do_bsd_symlinkat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg3); + ret = get_errno(symlinkat(p1, arg2, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg3); + + return ret; +} + +/* readlink(2) */ +static abi_long do_bsd_readlink(CPUArchState *env, abi_long arg1, + abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH(p1, arg1); + p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (p2 == NULL) { + UNLOCK_PATH(p1, arg1); + return -TARGET_EFAULT; + } + if (strcmp(p1, "/proc/curproc/file") == 0) { + CPUState *cpu = env_cpu(env); + TaskState *ts = (TaskState *)cpu->opaque; + strncpy(p2, ts->bprm->fullpath, arg3); + ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3); + } else { + ret = get_errno(readlink(path(p1), p2, arg3)); + } + unlock_user(p2, arg2, ret); + UNLOCK_PATH(p1, arg1); + + return ret; +} + +/* readlinkat(2) */ +static abi_long do_bsd_readlinkat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH(p1, arg2); + p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0); + if (p2 == NULL) { + UNLOCK_PATH(p1, arg2); + return -TARGET_EFAULT; + } + ret = get_errno(readlinkat(arg1, p1, p2, arg4)); + unlock_user(p2, arg3, ret); + UNLOCK_PATH(p1, arg2); + + return ret; +} + +/* chmod(2) */ +static abi_long do_bsd_chmod(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chmod(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchmod(2) */ +static abi_long do_bsd_fchmod(abi_long arg1, abi_long arg2) +{ + return get_errno(fchmod(arg1, arg2)); +} + +/* lchmod(2) */ +static abi_long do_bsd_lchmod(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lchmod(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchmodat(2) */ +static abi_long do_bsd_fchmodat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(fchmodat(arg1, p, arg3, arg4)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* pre-ino64 mknod(2) */ +static abi_long do_bsd_freebsd11_mknod(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(syscall(SYS_freebsd11_mknod, p, arg2, arg3)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* pre-ino64 mknodat(2) */ +static abi_long do_bsd_freebsd11_mknodat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(syscall(SYS_freebsd11_mknodat, arg1, p, arg3, arg4)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* post-ino64 mknodat(2) */ +static abi_long do_bsd_mknodat(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, + abi_long arg6) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + /* 32-bit arch's use two 32 registers for 64 bit return value */ + if (regpairs_aligned(cpu_env) != 0) { + ret = get_errno(mknodat(arg1, p, arg3, target_arg64(arg5, arg6))); + } else { + ret = get_errno(mknodat(arg1, p, arg3, target_arg64(arg4, arg5))); + } + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* chown(2) */ +static abi_long do_bsd_chown(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chown(p, arg2, arg3)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchown(2) */ +static abi_long do_bsd_fchown(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + return get_errno(fchown(arg1, arg2, arg3)); +} + +/* lchown(2) */ +static abi_long do_bsd_lchown(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lchown(p, arg2, arg3)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchownat(2) */ +static abi_long do_bsd_fchownat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(fchownat(arg1, p, arg3, arg4, arg5)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* chflags(2) */ +static abi_long do_bsd_chflags(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chflags(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* lchflags(2) */ +static abi_long do_bsd_lchflags(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lchflags(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchflags(2) */ +static abi_long do_bsd_fchflags(abi_long arg1, abi_long arg2) +{ + return get_errno(fchflags(arg1, arg2)); +} + +/* chroot(2) */ +static abi_long do_bsd_chroot(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chroot(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* flock(2) */ +static abi_long do_bsd_flock(abi_long arg1, abi_long arg2) +{ + return get_errno(flock(arg1, arg2)); +} + +/* mkfifo(2) */ +static abi_long do_bsd_mkfifo(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(mkfifo(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* mkfifoat(2) */ +static abi_long do_bsd_mkfifoat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(mkfifoat(arg1, p, arg3)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* pathconf(2) */ +static abi_long do_bsd_pathconf(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(pathconf(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* lpathconf(2) */ +static abi_long do_bsd_lpathconf(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lpathconf(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fpathconf(2) */ +static abi_long do_bsd_fpathconf(abi_long arg1, abi_long arg2) +{ + return get_errno(fpathconf(arg1, arg2)); +} + +/* undelete(2) */ +static abi_long do_bsd_undelete(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(undelete(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + #endif /* BSD_FILE_H */ diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c index 71aa0d38e03..57996cad8ae 100644 --- a/bsd-user/freebsd/os-syscall.c +++ b/bsd-user/freebsd/os-syscall.c @@ -32,7 +32,9 @@ #include "qemu/cutils.h" #include "qemu/path.h" #include +#include #include +#include #include #include @@ -44,6 +46,10 @@ #include "bsd-proc.h" /* I/O */ +safe_syscall3(int, open, const char *, path, int, flags, mode_t, mode); +safe_syscall4(int, openat, int, fd, const char *, path, int, flags, mode_t, + mode); + safe_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes); safe_syscall4(ssize_t, pread, int, fd, void *, buf, size_t, nbytes, off_t, offset); @@ -257,6 +263,234 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); break; + case TARGET_FREEBSD_NR_open: /* open(2) */ + ret = do_bsd_open(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_openat: /* openat(2) */ + ret = do_bsd_openat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_close: /* close(2) */ + ret = do_bsd_close(arg1); + break; + + case TARGET_FREEBSD_NR_fdatasync: /* fdatasync(2) */ + ret = do_bsd_fdatasync(arg1); + break; + + case TARGET_FREEBSD_NR_fsync: /* fsync(2) */ + ret = do_bsd_fsync(arg1); + break; + + case TARGET_FREEBSD_NR_freebsd12_closefrom: /* closefrom(2) */ + ret = do_bsd_closefrom(arg1); + break; + + case TARGET_FREEBSD_NR_revoke: /* revoke(2) */ + ret = do_bsd_revoke(arg1); + break; + + case TARGET_FREEBSD_NR_access: /* access(2) */ + ret = do_bsd_access(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_eaccess: /* eaccess(2) */ + ret = do_bsd_eaccess(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_faccessat: /* faccessat(2) */ + ret = do_bsd_faccessat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_chdir: /* chdir(2) */ + ret = do_bsd_chdir(arg1); + break; + + case TARGET_FREEBSD_NR_fchdir: /* fchdir(2) */ + ret = do_bsd_fchdir(arg1); + break; + + case TARGET_FREEBSD_NR_rename: /* rename(2) */ + ret = do_bsd_rename(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_renameat: /* renameat(2) */ + ret = do_bsd_renameat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_link: /* link(2) */ + ret = do_bsd_link(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_linkat: /* linkat(2) */ + ret = do_bsd_linkat(arg1, arg2, arg3, arg4, arg5); + break; + + case TARGET_FREEBSD_NR_unlink: /* unlink(2) */ + ret = do_bsd_unlink(arg1); + break; + + case TARGET_FREEBSD_NR_unlinkat: /* unlinkat(2) */ + ret = do_bsd_unlinkat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_mkdir: /* mkdir(2) */ + ret = do_bsd_mkdir(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mkdirat: /* mkdirat(2) */ + ret = do_bsd_mkdirat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_rmdir: /* rmdir(2) (XXX no rmdirat()?) */ + ret = do_bsd_rmdir(arg1); + break; + + case TARGET_FREEBSD_NR___getcwd: /* undocumented __getcwd() */ + ret = do_bsd___getcwd(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_dup: /* dup(2) */ + ret = do_bsd_dup(arg1); + break; + + case TARGET_FREEBSD_NR_dup2: /* dup2(2) */ + ret = do_bsd_dup2(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_truncate: /* truncate(2) */ + ret = do_bsd_truncate(cpu_env, arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_ftruncate: /* ftruncate(2) */ + ret = do_bsd_ftruncate(cpu_env, arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_acct: /* acct(2) */ + ret = do_bsd_acct(arg1); + break; + + case TARGET_FREEBSD_NR_sync: /* sync(2) */ + ret = do_bsd_sync(); + break; + + case TARGET_FREEBSD_NR_mount: /* mount(2) */ + ret = do_bsd_mount(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_unmount: /* unmount(2) */ + ret = do_bsd_unmount(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_nmount: /* nmount(2) */ + ret = do_bsd_nmount(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_symlink: /* symlink(2) */ + ret = do_bsd_symlink(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_symlinkat: /* symlinkat(2) */ + ret = do_bsd_symlinkat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_readlink: /* readlink(2) */ + ret = do_bsd_readlink(cpu_env, arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_readlinkat: /* readlinkat(2) */ + ret = do_bsd_readlinkat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_chmod: /* chmod(2) */ + ret = do_bsd_chmod(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fchmod: /* fchmod(2) */ + ret = do_bsd_fchmod(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lchmod: /* lchmod(2) */ + ret = do_bsd_lchmod(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fchmodat: /* fchmodat(2) */ + ret = do_bsd_fchmodat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_freebsd11_mknod: /* mknod(2) */ + ret = do_bsd_freebsd11_mknod(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_freebsd11_mknodat: /* mknodat(2) */ + ret = do_bsd_freebsd11_mknodat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_mknodat: /* mknodat(2) */ + ret = do_bsd_mknodat(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_chown: /* chown(2) */ + ret = do_bsd_chown(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_fchown: /* fchown(2) */ + ret = do_bsd_fchown(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_lchown: /* lchown(2) */ + ret = do_bsd_lchown(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_fchownat: /* fchownat(2) */ + ret = do_bsd_fchownat(arg1, arg2, arg3, arg4, arg5); + break; + + case TARGET_FREEBSD_NR_chflags: /* chflags(2) */ + ret = do_bsd_chflags(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lchflags: /* lchflags(2) */ + ret = do_bsd_lchflags(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fchflags: /* fchflags(2) */ + ret = do_bsd_fchflags(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_chroot: /* chroot(2) */ + ret = do_bsd_chroot(arg1); + break; + + case TARGET_FREEBSD_NR_flock: /* flock(2) */ + ret = do_bsd_flock(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mkfifo: /* mkfifo(2) */ + ret = do_bsd_mkfifo(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mkfifoat: /* mkfifoat(2) */ + ret = do_bsd_mkfifoat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_pathconf: /* pathconf(2) */ + ret = do_bsd_pathconf(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lpathconf: /* lpathconf(2) */ + ret = do_bsd_lpathconf(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fpathconf: /* fpathconf(2) */ + ret = do_bsd_fpathconf(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_undelete: /* undelete(2) */ + ret = do_bsd_undelete(arg1); + break; + default: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); ret = -TARGET_ENOSYS; diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index f5797b28e39..b6d113d24a7 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -226,4 +226,8 @@ type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ return safe_syscall(SYS_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ } +/* So far all target and host bitmasks are the same */ +#define target_to_host_bitmask(x, tbl) (x) +#define host_to_target_bitmask(x, tbl) (x) + #endif /* SYSCALL_DEFS_H */ diff --git a/common-user/meson.build b/common-user/meson.build index 26212dda5c7..ac9de5b9e3f 100644 --- a/common-user/meson.build +++ b/common-user/meson.build @@ -1,3 +1,7 @@ +if not have_user + subdir_done() +endif + common_user_inc += include_directories('host/' / host_arch) user_ss.add(files( diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index d0c603c54ec..db552f18390 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index 77944247459..dc78044fb15 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -3,4 +3,5 @@ TARGET_BASE_ARCH=arm TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index 3e10d6b15d5..7f5d65794c1 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -3,4 +3,5 @@ TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index a249cc2e29d..943d0d87bfd 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -4,4 +4,5 @@ TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak new file mode 100644 index 00000000000..7d1b9640209 --- /dev/null +++ b/configs/targets/loongarch64-linux-user.mak @@ -0,0 +1,3 @@ +# Default configuration for loongarch64-linux-user +TARGET_ARCH=loongarch64 +TARGET_BASE_ARCH=loongarch diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index 7bc06c850cb..483474ba93f 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -2,3 +2,4 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu64.xml +TARGET_NEED_FDT=y diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak index bd2f1fd4973..9761618e67f 100644 --- a/configs/targets/riscv32-linux-user.mak +++ b/configs/targets/riscv32-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak index 4aca7662cea..cfd1fd382f9 100644 --- a/configs/targets/riscv64-linux-user.mak +++ b/configs/targets/riscv64-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configure b/configure index dccf8fbdc06..52d879b1972 100755 --- a/configure +++ b/configure @@ -329,7 +329,7 @@ fi fdt="auto" # 2. Automatically enable/disable other options -tcg="enabled" +tcg="auto" cfi="false" # parse CC options second @@ -678,6 +678,21 @@ werror="" as_shared_lib="no" as_static_lib="no" +meson_option_build_array() { + printf '[' + (if test "$targetos" == windows; then + IFS=\; + else + IFS=: + fi + for e in $1; do + e=${e/'\'/'\\'} + e=${e/\"/'\"'} + printf '"""%s""",' "$e" + done) + printf ']\n' +} + . $source_path/scripts/meson-buildoptions.sh meson_options= @@ -1368,13 +1383,6 @@ static THREAD int tls_var; int main(void) { return tls_var; } EOF -# Check we support -fno-pie and -no-pie first; we will need the former for -# building ROMs, and both for everything if --disable-pie is passed. -if compile_prog "-Werror -fno-pie" "-no-pie"; then - CFLAGS_NOPIE="-fno-pie" - LDFLAGS_NOPIE="-no-pie" -fi - if test "$static" = "yes"; then if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" @@ -1387,8 +1395,10 @@ if test "$static" = "yes"; then pie="no" fi elif test "$pie" = "no"; then - CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS" - CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS" + if compile_prog "-Werror -fno-pie" "-no-pie"; then + CONFIGURE_CFLAGS="-fno-pie $CONFIGURE_CFLAGS" + CONFIGURE_LDFLAGS="-no-pie $CONFIGURE_LDFLAGS" + fi elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS" @@ -1431,11 +1441,6 @@ EOF fi fi -if test "$tcg" = "enabled"; then - git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" - git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" -fi - if test -z "${target_list+xxx}" ; then default_targets=yes for target in $default_target_list; do @@ -1466,6 +1471,19 @@ case " $target_list " in ;; esac +if test "$tcg" = "auto"; then + if test -z "$target_list"; then + tcg="disabled" + else + tcg="enabled" + fi +fi + +if test "$tcg" = "enabled"; then + git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" + git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" +fi + feature_not_found() { feature=$1 remedy=$2 @@ -1880,7 +1898,7 @@ fi : ${cross_cc_hexagon="hexagon-unknown-linux-musl-clang"} : ${cross_cc_cflags_hexagon="-mv67 -O2 -static"} : ${cross_cc_cflags_i386="-m32"} -: ${cross_cc_cflags_ppc="-m32"} +: ${cross_cc_cflags_ppc="-m32 -mbig-endian"} : ${cross_cc_cflags_ppc64="-m64 -mbig-endian"} : ${cross_cc_ppc64le="$cross_cc_ppc64"} : ${cross_cc_cflags_ppc64le="-m64 -mlittle-endian"} @@ -1890,6 +1908,7 @@ fi : ${cross_cc_cflags_x86_64="-m64"} compute_target_variable() { + eval "$2=" if eval test -n "\"\${cross_prefix_$1}\""; then if eval has "\"\${cross_prefix_$1}\$3\""; then eval "$2=\"\${cross_prefix_$1}\$3\"" @@ -1897,8 +1916,20 @@ compute_target_variable() { fi } +# probe_target_compiler TARGET +# +# Look for a compiler for the given target, either native or cross. +# Set variables target_* if a compiler is found, and container_cross_* +# if a Docker-based cross-compiler image is known for the target. +# Set got_cross_cc to yes/no depending on whether a non-container-based +# compiler was found. +# +# If TARGET is a user-mode emulation target, also set build_static to +# "y" if static linking is possible. +# probe_target_compiler() { # reset all output variables + got_cross_cc=no container_image= container_hosts= container_cross_cc= @@ -1909,16 +1940,9 @@ probe_target_compiler() { container_cross_objcopy= container_cross_ranlib= container_cross_strip= - target_cc= - target_ar= - target_as= - target_ld= - target_nm= - target_objcopy= - target_ranlib= - target_strip= - - case $1 in + + target_arch=${1%%-*} + case $target_arch in aarch64) container_hosts="x86_64 aarch64" ;; alpha) container_hosts=x86_64 ;; arm) container_hosts="x86_64 aarch64" ;; @@ -1926,6 +1950,7 @@ probe_target_compiler() { hexagon) container_hosts=x86_64 ;; hppa) container_hosts=x86_64 ;; i386) container_hosts=x86_64 ;; + loongarch64) container_hosts=x86_64 ;; m68k) container_hosts=x86_64 ;; microblaze) container_hosts=x86_64 ;; mips64el) container_hosts=x86_64 ;; @@ -1947,7 +1972,7 @@ probe_target_compiler() { for host in $container_hosts; do test "$container" != no || continue test "$host" = "$cpu" || continue - case $1 in + case $target_arch in aarch64) # We don't have any bigendian build tools so we only use this for AArch64 container_image=debian-arm64-cross @@ -1980,6 +2005,10 @@ probe_target_compiler() { container_image=fedora-i386-cross container_cross_prefix= ;; + 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- @@ -2063,54 +2092,116 @@ probe_target_compiler() { : ${container_cross_strip:=${container_cross_prefix}strip} done - eval "target_cflags=\${cross_cc_cflags_$1}" - if eval test -n "\"\${cross_cc_$1}\""; then - if eval has "\"\${cross_cc_$1}\""; then - eval "target_cc=\"\${cross_cc_$1}\"" + local t try + 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 + eval "target_cflags=\${cross_cc_cflags_$target_arch}" + for t in $try; do + case $t in + native) + target_cc=$cc + target_ccas=$ccas + target_ar=$ar + target_as=$as + target_ld=$ld + target_nm=$nm + target_objcopy=$objcopy + target_ranlib=$ranlib + target_strip=$strip + ;; + cross) + target_cc= + if eval test -n "\"\${cross_cc_$target_arch}\""; then + if eval has "\"\${cross_cc_$target_arch}\""; then + eval "target_cc=\"\${cross_cc_$target_arch}\"" + fi + else + compute_target_variable $target_arch target_cc gcc + fi + target_ccas=$target_cc + compute_target_variable $target_arch target_ar ar + compute_target_variable $target_arch target_as as + compute_target_variable $target_arch target_ld ld + compute_target_variable $target_arch target_nm nm + compute_target_variable $target_arch target_objcopy objcopy + compute_target_variable $target_arch target_ranlib ranlib + compute_target_variable $target_arch target_strip strip + ;; + esac + + if test -n "$target_cc"; then + case $target_arch in + i386|x86_64) + if $target_cc --version | grep -qi "clang"; then + continue + fi + ;; + esac + elif test -n "$target_as" && test -n "$target_ld"; then + # Special handling for assembler only targets + case $target in + tricore-softmmu) + build_static= + got_cross_cc=yes + break + ;; + *) + continue + ;; + esac + else + continue fi - else - compute_target_variable $1 target_cc gcc - fi - target_ccas=$target_cc - compute_target_variable $1 target_ar ar - compute_target_variable $1 target_as as - compute_target_variable $1 target_ld ld - compute_target_variable $1 target_nm nm - compute_target_variable $1 target_objcopy objcopy - compute_target_variable $1 target_ranlib ranlib - compute_target_variable $1 target_strip strip - if test "$1" = $cpu; then - : ${target_cc:=$cc} - : ${target_ccas:=$ccas} - : ${target_as:=$as} - : ${target_ld:=$ld} - : ${target_ar:=$ar} - : ${target_as:=$as} - : ${target_ld:=$ld} - : ${target_nm:=$nm} - : ${target_objcopy:=$objcopy} - : ${target_ranlib:=$ranlib} - : ${target_strip:=$strip} - fi - if test -n "$target_cc"; then + + write_c_skeleton case $1 in - i386|x86_64) - if $target_cc --version | grep -qi "clang"; then - unset target_cc + *-softmmu) + if do_compiler "$target_cc" $target_cflags -o $TMPO -c $TMPC && + do_compiler "$target_cc" $target_cflags -r -nostdlib -o "${TMPDIR1}/${TMPB}2.o" "$TMPO" -lgcc; then + got_cross_cc=yes + break + fi + ;; + *) + if do_compiler "$target_cc" $target_cflags -o $TMPE $TMPC -static ; then + build_static=y + got_cross_cc=yes + break + fi + if do_compiler "$target_cc" $target_cflags -o $TMPE $TMPC ; then + build_static= + got_cross_cc=yes + break fi ;; esac - fi -} - -probe_target_compilers() { - for i; do - probe_target_compiler $i - test -n "$target_cc" && return 0 done + if test $got_cross_cc != yes; then + build_static= + target_cc= + target_ccas= + target_cflags= + target_ar= + target_as= + target_ld= + target_nm= + target_objcopy= + target_ranlib= + target_strip= + fi } write_target_makefile() { + echo "EXTRA_CFLAGS=$target_cflags" if test -n "$target_cc"; then echo "CC=$target_cc" echo "CCAS=$target_ccas" @@ -2139,6 +2230,7 @@ write_target_makefile() { } write_container_target_makefile() { + echo "EXTRA_CFLAGS=$target_cflags" if test -n "$container_cross_cc"; then echo "CC=\$(DOCKER_SCRIPT) cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" echo "CCAS=\$(DOCKER_SCRIPT) cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" @@ -2260,7 +2352,7 @@ done # Mac OS X ships with a broken assembler roms= -probe_target_compilers i386 x86_64 +probe_target_compiler i386-softmmu if test -n "$target_cc" && test "$targetos" != "darwin" && test "$targetos" != "sunos" && \ test "$targetos" != "haiku" && test "$softmmu" = yes ; then @@ -2274,7 +2366,7 @@ if test -n "$target_cc" && fi done if test -n "$ld_i386_emulation"; then - roms="optionrom" + roms="pc-bios/optionrom" config_mak=pc-bios/optionrom/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "TOPSRC_DIR=$source_path" >> $config_mak @@ -2283,9 +2375,9 @@ if test -n "$target_cc" && fi fi -probe_target_compilers ppc ppc64 +probe_target_compiler ppc-softmmu if test -n "$target_cc" && test "$softmmu" = yes; then - roms="$roms vof" + roms="$roms pc-bios/vof" config_mak=pc-bios/vof/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_DIR=$source_path/pc-bios/vof" >> $config_mak @@ -2294,7 +2386,7 @@ fi # Only build s390-ccw bios if the compiler has -march=z900 or -march=z10 # (which is the lowest architecture level that Clang supports) -probe_target_compiler s390x +probe_target_compiler s390x-softmmu if test -n "$target_cc" && test "$softmmu" = yes; then write_c_skeleton do_compiler "$target_cc" $target_cc_cflags -march=z900 -o $TMPO -c $TMPC @@ -2304,7 +2396,7 @@ if test -n "$target_cc" && test "$softmmu" = yes; then echo "WARNING: Your compiler does not support the z900!" echo " The s390-ccw bios will only work with guest CPUs >= z10." fi - roms="$roms s390-ccw" + roms="$roms pc-bios/s390-ccw" config_mak=pc-bios/s390-ccw/config-host.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/pc-bios/s390-ccw" >> $config_mak @@ -2514,7 +2606,6 @@ tcg_tests_targets= for target in $target_list; do arch=${target%%-*} - probe_target_compiler ${arch} config_target_mak=tests/tcg/config-$target.mak echo "# Automatically generated by configure - do not modify" > $config_target_mak @@ -2533,29 +2624,7 @@ for target in $target_list; do ;; esac - got_cross_cc=no - unset build_static - - if test -n "$target_cc"; then - write_c_skeleton - if ! do_compiler "$target_cc" $target_cflags \ - -o $TMPE $TMPC -static ; then - # For host systems we might get away with building without -static - if do_compiler "$target_cc" $target_cflags \ - -o $TMPE $TMPC ; then - got_cross_cc=yes - fi - else - got_cross_cc=yes - build_static=y - fi - elif test -n "$target_as" && test -n "$target_ld"; then - # Special handling for assembler only tests - case $target in - tricore-softmmu) got_cross_cc=yes ;; - esac - fi - + probe_target_compiler $target if test $got_cross_cc = yes; then # Test for compiler features for optional tests. We only do this # for cross compilers because ensuring the docker containers based @@ -2629,7 +2698,6 @@ for target in $target_list; do if test $got_cross_cc = yes; then mkdir -p tests/tcg/$target echo "QEMU=$PWD/$qemu" >> $config_target_mak - echo "EXTRA_CFLAGS=$target_cflags" >> $config_target_mak echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> $makefile tcg_tests_targets="$tcg_tests_targets $target" fi @@ -2763,13 +2831,12 @@ preserve_env CC preserve_env CFLAGS preserve_env CXX preserve_env CXXFLAGS -preserve_env INSTALL preserve_env LD preserve_env LDFLAGS preserve_env LD_LIBRARY_PATH -preserve_env LIBTOOL preserve_env MAKE preserve_env NM +preserve_env OBJCFLAGS preserve_env OBJCOPY preserve_env PATH preserve_env PKG_CONFIG diff --git a/contrib/vhost-user-blk/meson.build b/contrib/vhost-user-blk/meson.build index 601ea15ef54..dcb9e2ffcd0 100644 --- a/contrib/vhost-user-blk/meson.build +++ b/contrib/vhost-user-blk/meson.build @@ -1,5 +1,4 @@ -# FIXME: broken on 32-bit architectures executable('vhost-user-blk', files('vhost-user-blk.c'), dependencies: [qemuutil, vhost_user], - build_by_default: false, + build_by_default: targetos == 'linux', install: false) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index cd4a5d7335a..9cb78ca1d0d 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -146,7 +146,7 @@ vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt) req->size = vub_iov_size(iov, iovcnt); rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512); if (rc < 0) { - fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n", + fprintf(stderr, "%s, Sector %"PRIu64", Size %zu failed with %s\n", vdev_blk->blk_name, req->sector_num, req->size, strerror(errno)); return -1; @@ -169,7 +169,7 @@ vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt) req->size = vub_iov_size(iov, iovcnt); rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512); if (rc < 0) { - fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n", + fprintf(stderr, "%s, Sector %"PRIu64", Size %zu failed with %s\n", vdev_blk->blk_name, req->sector_num, req->size, strerror(errno)); return -1; @@ -188,7 +188,7 @@ vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt, size = vub_iov_size(iov, iovcnt); if (size != sizeof(*desc)) { - fprintf(stderr, "Invalid size %ld, expect %ld\n", size, sizeof(*desc)); + fprintf(stderr, "Invalid size %zd, expect %zd\n", size, sizeof(*desc)); return -1; } buf = g_new0(char, size); diff --git a/cpu.c b/cpu.c index 8f6a852b573..58af550e3de 100644 --- a/cpu.c +++ b/cpu.c @@ -70,15 +70,21 @@ struct libafl_hook { struct libafl_hook* libafl_qemu_hooks[LIBAFL_TABLES_SIZE]; size_t libafl_qemu_hooks_num = 0; -__thread CPUArchState *libafl_qemu_env; +__thread int libafl_valid_current_cpu = 0; void libafl_helper_table_add(TCGHelperInfo* info); -static GByteArray *libafl_qemu_mem_buf = NULL; +static __thread GByteArray *libafl_qemu_mem_buf = NULL; + +CPUState* libafl_qemu_get_cpu(int cpu_index); +int libafl_qemu_num_cpus(void); +CPUState* libafl_qemu_current_cpu(void); +int libafl_qemu_cpu_index(CPUState*); + +int libafl_qemu_write_reg(CPUState* cpu, int reg, uint8_t* val); +int libafl_qemu_read_reg(CPUState* cpu, int reg, uint8_t* val); +int libafl_qemu_num_regs(CPUState* cpu); -int libafl_qemu_write_reg(int reg, uint8_t* val); -int libafl_qemu_read_reg(int reg, uint8_t* val); -int libafl_qemu_num_regs(void); int libafl_qemu_set_breakpoint(target_ulong addr); int libafl_qemu_remove_breakpoint(target_ulong addr); size_t libafl_qemu_set_hook(target_ulong pc, void (*callback)(target_ulong, uint64_t), @@ -88,16 +94,54 @@ int libafl_qemu_remove_hook(size_t num, int invalidate); struct libafl_hook* libafl_search_hook(target_ulong addr); void libafl_flush_jit(void); -int libafl_qemu_write_reg(int reg, uint8_t* val) +/* +void* libafl_qemu_g2h(CPUState *cpu, target_ulong x); +target_ulong libafl_qemu_h2g(CPUState *cpu, void* x); + +void* libafl_qemu_g2h(CPUState *cpu, target_ulong x) { - CPUState *cpu = current_cpu; - if (!cpu) { - cpu = env_cpu(libafl_qemu_env); - if (!cpu) { - return 0; - } + return g2h(cpu, x); +} + +target_ulong libafl_qemu_h2g(CPUState *cpu, void* x) +{ + return h2g(cpu, x); +} +*/ + +CPUState* libafl_qemu_get_cpu(int cpu_index) +{ + CPUState *cpu; + CPU_FOREACH(cpu) { + if (cpu->cpu_index == cpu_index) + return cpu; } + return NULL; +} +int libafl_qemu_num_cpus(void) +{ + CPUState *cpu; + int num = 0; + CPU_FOREACH(cpu) { + num++; + } + return num; +} + +CPUState* libafl_qemu_current_cpu(void) +{ + return current_cpu; +} + +int libafl_qemu_cpu_index(CPUState* cpu) +{ + if (cpu) return cpu->cpu_index; + return -1; +} + +int libafl_qemu_write_reg(CPUState* cpu, int reg, uint8_t* val) +{ CPUClass *cc = CPU_GET_CLASS(cpu); if (reg < cc->gdb_num_core_regs) { return cc->gdb_write_register(cpu, val, reg); @@ -105,16 +149,8 @@ int libafl_qemu_write_reg(int reg, uint8_t* val) return 0; } -int libafl_qemu_read_reg(int reg, uint8_t* val) +int libafl_qemu_read_reg(CPUState* cpu, int reg, uint8_t* val) { - CPUState *cpu = current_cpu; - if (!cpu) { - cpu = env_cpu(libafl_qemu_env); - if (!cpu) { - return 0; - } - } - if (libafl_qemu_mem_buf == NULL) { libafl_qemu_mem_buf = g_byte_array_sized_new(64); } @@ -131,16 +167,8 @@ int libafl_qemu_read_reg(int reg, uint8_t* val) return 0; } -int libafl_qemu_num_regs(void) +int libafl_qemu_num_regs(CPUState* cpu) { - CPUState *cpu = current_cpu; - if (!cpu) { - cpu = env_cpu(libafl_qemu_env); - if (!cpu) { - return 0; - } - } - CPUClass *cc = CPU_GET_CLASS(cpu); return cc->gdb_num_core_regs; } diff --git a/cpus-common.c b/cpus-common.c index db459b41ce2..793364dc0ed 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -73,6 +73,12 @@ static int cpu_get_free_index(void) } 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) { @@ -84,6 +90,7 @@ void cpu_list_add(CPUState *cpu) assert(!cpu_index_auto_assigned); } QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); + cpu_list_generation_id++; } void cpu_list_remove(CPUState *cpu) @@ -96,6 +103,7 @@ void cpu_list_remove(CPUState *cpu) QTAILQ_REMOVE_RCU(&cpus, cpu, node); cpu->cpu_index = UNASSIGNED_CPU_INDEX; + cpu_list_generation_id++; } CPUState *qemu_get_cpu(int index) diff --git a/crypto/block-luks.c b/crypto/block-luks.c index fe8f04ffb29..f62be6836b1 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -495,7 +495,7 @@ qcrypto_block_luks_load_header(QCryptoBlock *block, void *opaque, Error **errp) { - ssize_t rv; + int rv; size_t i; QCryptoBlockLUKS *luks = block->opaque; @@ -856,7 +856,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, splitkey, splitkeylen, opaque, - errp) != splitkeylen) { + errp) < 0) { goto cleanup; } @@ -903,7 +903,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block, g_autofree uint8_t *splitkey = NULL; size_t splitkeylen; g_autofree uint8_t *possiblekey = NULL; - ssize_t rv; + int rv; g_autoptr(QCryptoCipher) cipher = NULL; uint8_t keydigest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; g_autoptr(QCryptoIVGen) ivgen = NULL; @@ -1193,7 +1193,7 @@ qcrypto_block_luks_erase_key(QCryptoBlock *block, garbagesplitkey, splitkeylen, opaque, - &local_err) != splitkeylen) { + &local_err) < 0) { error_propagate(errp, local_err); return -1; } diff --git a/crypto/block.c b/crypto/block.c index eb057948b59..7bb4b74a37c 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -115,7 +115,7 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, } -static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, +static int qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, Error **errp) { size_t *headerlenp = opaque; @@ -126,12 +126,12 @@ static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, } -static ssize_t qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block, +static int qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block, size_t offset, const uint8_t *buf, size_t buflen, void *opaque, Error **errp) { /* Discard the bytes, we're not actually writing to an image */ - return buflen; + return 0; } diff --git a/disas.c b/disas.c index b2753e19025..e31438f3497 100644 --- a/disas.c +++ b/disas.c @@ -178,9 +178,6 @@ static void initialize_debug_host(CPUDebug *s) #endif #elif defined(__aarch64__) s->info.cap_arch = CS_ARCH_ARM64; -# ifdef CONFIG_ARM_A64_DIS - s->info.print_insn = print_insn_arm_a64; -# endif #elif defined(__alpha__) s->info.print_insn = print_insn_alpha; #elif defined(__sparc__) diff --git a/disas/arm-a64.cc b/disas/arm-a64.cc deleted file mode 100644 index a1402a2e071..00000000000 --- a/disas/arm-a64.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * ARM A64 disassembly output wrapper to libvixl - * Copyright (c) 2013 Linaro Limited - * Written by Claudio Fontana - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "qemu/osdep.h" -#include "disas/dis-asm.h" - -#include "vixl/a64/disasm-a64.h" - -using namespace vixl; - -static Decoder *vixl_decoder = NULL; -static Disassembler *vixl_disasm = NULL; - -/* We don't use libvixl's PrintDisassembler because its output - * is a little unhelpful (trailing newlines, for example). - * Instead we use our own very similar variant so we have - * control over the format. - */ -class QEMUDisassembler : public Disassembler { -public: - QEMUDisassembler() : printf_(NULL), stream_(NULL) { } - ~QEMUDisassembler() { } - - void SetStream(FILE *stream) { - stream_ = stream; - } - - void SetPrintf(fprintf_function printf_fn) { - printf_ = printf_fn; - } - -protected: - virtual void ProcessOutput(const Instruction *instr) { - printf_(stream_, "%08" PRIx32 " %s", - instr->InstructionBits(), GetOutput()); - } - -private: - fprintf_function printf_; - FILE *stream_; -}; - -static int vixl_is_initialized(void) -{ - return vixl_decoder != NULL; -} - -static void vixl_init() { - vixl_decoder = new Decoder(); - vixl_disasm = new QEMUDisassembler(); - vixl_decoder->AppendVisitor(vixl_disasm); -} - -#define INSN_SIZE 4 - -/* Disassemble ARM A64 instruction. This is our only entry - * point from QEMU's C code. - */ -int print_insn_arm_a64(uint64_t addr, disassemble_info *info) -{ - uint8_t bytes[INSN_SIZE]; - uint32_t instrval; - const Instruction *instr; - int status; - - status = info->read_memory_func(addr, bytes, INSN_SIZE, info); - if (status != 0) { - info->memory_error_func(status, addr, info); - return -1; - } - - if (!vixl_is_initialized()) { - vixl_init(); - } - - ((QEMUDisassembler *)vixl_disasm)->SetPrintf(info->fprintf_func); - ((QEMUDisassembler *)vixl_disasm)->SetStream(info->stream); - - instrval = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; - instr = reinterpret_cast(&instrval); - vixl_disasm->MapCodeAddress(addr, instr); - vixl_decoder->Decode(instr); - - return INSN_SIZE; -} diff --git a/disas/libvixl/LICENCE b/disas/libvixl/LICENCE deleted file mode 100644 index b7e160a3f58..00000000000 --- a/disas/libvixl/LICENCE +++ /dev/null @@ -1,30 +0,0 @@ -LICENCE -======= - -The software in this repository is covered by the following licence. - -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/disas/libvixl/README b/disas/libvixl/README deleted file mode 100644 index 932a41adf7f..00000000000 --- a/disas/libvixl/README +++ /dev/null @@ -1,11 +0,0 @@ - -The code in this directory is a subset of libvixl: - https://github.com/armvixl/vixl -(specifically, it is the set of files needed for disassembly only, -taken from libvixl 1.12). -Bugfixes should preferably be sent upstream initially. - -The disassembler does not currently support the entire A64 instruction -set. Notably: - * Limited support for system instructions. - * A few miscellaneous integer and floating point instructions are missing. diff --git a/disas/libvixl/meson.build b/disas/libvixl/meson.build deleted file mode 100644 index 5e2eb33e8ef..00000000000 --- a/disas/libvixl/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -libvixl_ss.add(files( - 'vixl/a64/decoder-a64.cc', - 'vixl/a64/disasm-a64.cc', - 'vixl/a64/instructions-a64.cc', - 'vixl/compiler-intrinsics.cc', - 'vixl/utils.cc', -)) diff --git a/disas/libvixl/vixl/a64/assembler-a64.h b/disas/libvixl/vixl/a64/assembler-a64.h deleted file mode 100644 index fda5ccc6c75..00000000000 --- a/disas/libvixl/vixl/a64/assembler-a64.h +++ /dev/null @@ -1,4624 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_ASSEMBLER_A64_H_ -#define VIXL_A64_ASSEMBLER_A64_H_ - - -#include "vixl/globals.h" -#include "vixl/invalset.h" -#include "vixl/utils.h" -#include "vixl/code-buffer.h" -#include "vixl/a64/instructions-a64.h" - -namespace vixl { - -typedef uint64_t RegList; -static const int kRegListSizeInBits = sizeof(RegList) * 8; - - -// Registers. - -// Some CPURegister methods can return Register or VRegister types, so we need -// to declare them in advance. -class Register; -class VRegister; - -class CPURegister { - public: - enum RegisterType { - // The kInvalid value is used to detect uninitialized static instances, - // which are always zero-initialized before any constructors are called. - kInvalid = 0, - kRegister, - kVRegister, - kFPRegister = kVRegister, - kNoRegister - }; - - CPURegister() : code_(0), size_(0), type_(kNoRegister) { - VIXL_ASSERT(!IsValid()); - VIXL_ASSERT(IsNone()); - } - - CPURegister(unsigned code, unsigned size, RegisterType type) - : code_(code), size_(size), type_(type) { - VIXL_ASSERT(IsValidOrNone()); - } - - unsigned code() const { - VIXL_ASSERT(IsValid()); - return code_; - } - - RegisterType type() const { - VIXL_ASSERT(IsValidOrNone()); - return type_; - } - - RegList Bit() const { - VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); - return IsValid() ? (static_cast(1) << code_) : 0; - } - - unsigned size() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - int SizeInBytes() const { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(size() % 8 == 0); - return size_ / 8; - } - - int SizeInBits() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - bool Is8Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 8; - } - - bool Is16Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 16; - } - - bool Is32Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 32; - } - - bool Is64Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 64; - } - - bool Is128Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 128; - } - - bool IsValid() const { - if (IsValidRegister() || IsValidVRegister()) { - VIXL_ASSERT(!IsNone()); - return true; - } else { - // This assert is hit when the register has not been properly initialized. - // One cause for this can be an initialisation order fiasco. See - // https://isocpp.org/wiki/faq/ctors#static-init-order for some details. - VIXL_ASSERT(IsNone()); - return false; - } - } - - bool IsValidRegister() const { - return IsRegister() && - ((size_ == kWRegSize) || (size_ == kXRegSize)) && - ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); - } - - bool IsValidVRegister() const { - return IsVRegister() && - ((size_ == kBRegSize) || (size_ == kHRegSize) || - (size_ == kSRegSize) || (size_ == kDRegSize) || - (size_ == kQRegSize)) && - (code_ < kNumberOfVRegisters); - } - - bool IsValidFPRegister() const { - return IsFPRegister() && (code_ < kNumberOfVRegisters); - } - - bool IsNone() const { - // kNoRegister types should always have size 0 and code 0. - VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); - VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); - - return type_ == kNoRegister; - } - - bool Aliases(const CPURegister& other) const { - VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return (code_ == other.code_) && (type_ == other.type_); - } - - bool Is(const CPURegister& other) const { - VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return Aliases(other) && (size_ == other.size_); - } - - bool IsZero() const { - VIXL_ASSERT(IsValid()); - return IsRegister() && (code_ == kZeroRegCode); - } - - bool IsSP() const { - VIXL_ASSERT(IsValid()); - return IsRegister() && (code_ == kSPRegInternalCode); - } - - bool IsRegister() const { - return type_ == kRegister; - } - - bool IsVRegister() const { - return type_ == kVRegister; - } - - bool IsFPRegister() const { - return IsS() || IsD(); - } - - bool IsW() const { return IsValidRegister() && Is32Bits(); } - bool IsX() const { return IsValidRegister() && Is64Bits(); } - - // These assertions ensure that the size and type of the register are as - // described. They do not consider the number of lanes that make up a vector. - // So, for example, Is8B() implies IsD(), and Is1D() implies IsD, but IsD() - // does not imply Is1D() or Is8B(). - // Check the number of lanes, ie. the format of the vector, using methods such - // as Is8B(), Is1D(), etc. in the VRegister class. - bool IsV() const { return IsVRegister(); } - bool IsB() const { return IsV() && Is8Bits(); } - bool IsH() const { return IsV() && Is16Bits(); } - bool IsS() const { return IsV() && Is32Bits(); } - bool IsD() const { return IsV() && Is64Bits(); } - bool IsQ() const { return IsV() && Is128Bits(); } - - const Register& W() const; - const Register& X() const; - const VRegister& V() const; - const VRegister& B() const; - const VRegister& H() const; - const VRegister& S() const; - const VRegister& D() const; - const VRegister& Q() const; - - bool IsSameSizeAndType(const CPURegister& other) const { - return (size_ == other.size_) && (type_ == other.type_); - } - - protected: - unsigned code_; - unsigned size_; - RegisterType type_; - - private: - bool IsValidOrNone() const { - return IsValid() || IsNone(); - } -}; - - -class Register : public CPURegister { - public: - Register() : CPURegister() {} - explicit Register(const CPURegister& other) - : CPURegister(other.code(), other.size(), other.type()) { - VIXL_ASSERT(IsValidRegister()); - } - Register(unsigned code, unsigned size) - : CPURegister(code, size, kRegister) {} - - bool IsValid() const { - VIXL_ASSERT(IsRegister() || IsNone()); - return IsValidRegister(); - } - - static const Register& WRegFromCode(unsigned code); - static const Register& XRegFromCode(unsigned code); - - private: - static const Register wregisters[]; - static const Register xregisters[]; -}; - - -class VRegister : public CPURegister { - public: - VRegister() : CPURegister(), lanes_(1) {} - explicit VRegister(const CPURegister& other) - : CPURegister(other.code(), other.size(), other.type()), lanes_(1) { - VIXL_ASSERT(IsValidVRegister()); - VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); - } - VRegister(unsigned code, unsigned size, unsigned lanes = 1) - : CPURegister(code, size, kVRegister), lanes_(lanes) { - VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); - } - VRegister(unsigned code, VectorFormat format) - : CPURegister(code, RegisterSizeInBitsFromFormat(format), kVRegister), - lanes_(IsVectorFormat(format) ? LaneCountFromFormat(format) : 1) { - VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); - } - - bool IsValid() const { - VIXL_ASSERT(IsVRegister() || IsNone()); - return IsValidVRegister(); - } - - static const VRegister& BRegFromCode(unsigned code); - static const VRegister& HRegFromCode(unsigned code); - static const VRegister& SRegFromCode(unsigned code); - static const VRegister& DRegFromCode(unsigned code); - static const VRegister& QRegFromCode(unsigned code); - static const VRegister& VRegFromCode(unsigned code); - - VRegister V8B() const { return VRegister(code_, kDRegSize, 8); } - VRegister V16B() const { return VRegister(code_, kQRegSize, 16); } - VRegister V4H() const { return VRegister(code_, kDRegSize, 4); } - VRegister V8H() const { return VRegister(code_, kQRegSize, 8); } - VRegister V2S() const { return VRegister(code_, kDRegSize, 2); } - VRegister V4S() const { return VRegister(code_, kQRegSize, 4); } - VRegister V2D() const { return VRegister(code_, kQRegSize, 2); } - VRegister V1D() const { return VRegister(code_, kDRegSize, 1); } - - bool Is8B() const { return (Is64Bits() && (lanes_ == 8)); } - bool Is16B() const { return (Is128Bits() && (lanes_ == 16)); } - bool Is4H() const { return (Is64Bits() && (lanes_ == 4)); } - bool Is8H() const { return (Is128Bits() && (lanes_ == 8)); } - bool Is2S() const { return (Is64Bits() && (lanes_ == 2)); } - bool Is4S() const { return (Is128Bits() && (lanes_ == 4)); } - bool Is1D() const { return (Is64Bits() && (lanes_ == 1)); } - bool Is2D() const { return (Is128Bits() && (lanes_ == 2)); } - - // For consistency, we assert the number of lanes of these scalar registers, - // even though there are no vectors of equivalent total size with which they - // could alias. - bool Is1B() const { - VIXL_ASSERT(!(Is8Bits() && IsVector())); - return Is8Bits(); - } - bool Is1H() const { - VIXL_ASSERT(!(Is16Bits() && IsVector())); - return Is16Bits(); - } - bool Is1S() const { - VIXL_ASSERT(!(Is32Bits() && IsVector())); - return Is32Bits(); - } - - bool IsLaneSizeB() const { return LaneSizeInBits() == kBRegSize; } - bool IsLaneSizeH() const { return LaneSizeInBits() == kHRegSize; } - bool IsLaneSizeS() const { return LaneSizeInBits() == kSRegSize; } - bool IsLaneSizeD() const { return LaneSizeInBits() == kDRegSize; } - - int lanes() const { - return lanes_; - } - - bool IsScalar() const { - return lanes_ == 1; - } - - bool IsVector() const { - return lanes_ > 1; - } - - bool IsSameFormat(const VRegister& other) const { - return (size_ == other.size_) && (lanes_ == other.lanes_); - } - - unsigned LaneSizeInBytes() const { - return SizeInBytes() / lanes_; - } - - unsigned LaneSizeInBits() const { - return LaneSizeInBytes() * 8; - } - - private: - static const VRegister bregisters[]; - static const VRegister hregisters[]; - static const VRegister sregisters[]; - static const VRegister dregisters[]; - static const VRegister qregisters[]; - static const VRegister vregisters[]; - int lanes_; -}; - - -// Backward compatibility for FPRegisters. -typedef VRegister FPRegister; - -// No*Reg is used to indicate an unused argument, or an error case. Note that -// these all compare equal (using the Is() method). The Register and VRegister -// variants are provided for convenience. -const Register NoReg; -const VRegister NoVReg; -const FPRegister NoFPReg; // For backward compatibility. -const CPURegister NoCPUReg; - - -#define DEFINE_REGISTERS(N) \ -const Register w##N(N, kWRegSize); \ -const Register x##N(N, kXRegSize); -REGISTER_CODE_LIST(DEFINE_REGISTERS) -#undef DEFINE_REGISTERS -const Register wsp(kSPRegInternalCode, kWRegSize); -const Register sp(kSPRegInternalCode, kXRegSize); - - -#define DEFINE_VREGISTERS(N) \ -const VRegister b##N(N, kBRegSize); \ -const VRegister h##N(N, kHRegSize); \ -const VRegister s##N(N, kSRegSize); \ -const VRegister d##N(N, kDRegSize); \ -const VRegister q##N(N, kQRegSize); \ -const VRegister v##N(N, kQRegSize); -REGISTER_CODE_LIST(DEFINE_VREGISTERS) -#undef DEFINE_VREGISTERS - - -// Registers aliases. -const Register ip0 = x16; -const Register ip1 = x17; -const Register lr = x30; -const Register xzr = x31; -const Register wzr = w31; - - -// AreAliased returns true if any of the named registers overlap. Arguments -// set to NoReg are ignored. The system stack pointer may be specified. -bool AreAliased(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoReg, - const CPURegister& reg4 = NoReg, - const CPURegister& reg5 = NoReg, - const CPURegister& reg6 = NoReg, - const CPURegister& reg7 = NoReg, - const CPURegister& reg8 = NoReg); - - -// AreSameSizeAndType returns true if all of the specified registers have the -// same size, and are of the same type. The system stack pointer may be -// specified. Arguments set to NoReg are ignored, as are any subsequent -// arguments. At least one argument (reg1) must be valid (not NoCPUReg). -bool AreSameSizeAndType(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoCPUReg, - const CPURegister& reg4 = NoCPUReg, - const CPURegister& reg5 = NoCPUReg, - const CPURegister& reg6 = NoCPUReg, - const CPURegister& reg7 = NoCPUReg, - const CPURegister& reg8 = NoCPUReg); - - -// AreSameFormat returns true if all of the specified VRegisters have the same -// vector format. Arguments set to NoReg are ignored, as are any subsequent -// arguments. At least one argument (reg1) must be valid (not NoVReg). -bool AreSameFormat(const VRegister& reg1, - const VRegister& reg2, - const VRegister& reg3 = NoVReg, - const VRegister& reg4 = NoVReg); - - -// AreConsecutive returns true if all of the specified VRegisters are -// consecutive in the register file. Arguments set to NoReg are ignored, as are -// any subsequent arguments. At least one argument (reg1) must be valid -// (not NoVReg). -bool AreConsecutive(const VRegister& reg1, - const VRegister& reg2, - const VRegister& reg3 = NoVReg, - const VRegister& reg4 = NoVReg); - - -// Lists of registers. -class CPURegList { - public: - explicit CPURegList(CPURegister reg1, - CPURegister reg2 = NoCPUReg, - CPURegister reg3 = NoCPUReg, - CPURegister reg4 = NoCPUReg) - : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), - size_(reg1.size()), type_(reg1.type()) { - VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); - VIXL_ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) - : list_(list), size_(size), type_(type) { - VIXL_ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, - unsigned first_reg, unsigned last_reg) - : size_(size), type_(type) { - VIXL_ASSERT(((type == CPURegister::kRegister) && - (last_reg < kNumberOfRegisters)) || - ((type == CPURegister::kVRegister) && - (last_reg < kNumberOfVRegisters))); - VIXL_ASSERT(last_reg >= first_reg); - list_ = (UINT64_C(1) << (last_reg + 1)) - 1; - list_ &= ~((UINT64_C(1) << first_reg) - 1); - VIXL_ASSERT(IsValid()); - } - - CPURegister::RegisterType type() const { - VIXL_ASSERT(IsValid()); - return type_; - } - - // Combine another CPURegList into this one. Registers that already exist in - // this list are left unchanged. The type and size of the registers in the - // 'other' list must match those in this list. - void Combine(const CPURegList& other) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.RegisterSizeInBits() == size_); - list_ |= other.list(); - } - - // Remove every register in the other CPURegList from this one. Registers that - // do not exist in this list are ignored. The type and size of the registers - // in the 'other' list must match those in this list. - void Remove(const CPURegList& other) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.RegisterSizeInBits() == size_); - list_ &= ~other.list(); - } - - // Variants of Combine and Remove which take a single register. - void Combine(const CPURegister& other) { - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.size() == size_); - Combine(other.code()); - } - - void Remove(const CPURegister& other) { - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.size() == size_); - Remove(other.code()); - } - - // Variants of Combine and Remove which take a single register by its code; - // the type and size of the register is inferred from this list. - void Combine(int code) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); - list_ |= (UINT64_C(1) << code); - } - - void Remove(int code) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); - list_ &= ~(UINT64_C(1) << code); - } - - static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { - VIXL_ASSERT(list_1.type_ == list_2.type_); - VIXL_ASSERT(list_1.size_ == list_2.size_); - return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); - } - static CPURegList Union(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3); - static CPURegList Union(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3, - const CPURegList& list_4); - - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2) { - VIXL_ASSERT(list_1.type_ == list_2.type_); - VIXL_ASSERT(list_1.size_ == list_2.size_); - return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); - } - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3); - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3, - const CPURegList& list_4); - - bool Overlaps(const CPURegList& other) const { - return (type_ == other.type_) && ((list_ & other.list_) != 0); - } - - RegList list() const { - VIXL_ASSERT(IsValid()); - return list_; - } - - void set_list(RegList new_list) { - VIXL_ASSERT(IsValid()); - list_ = new_list; - } - - // Remove all callee-saved registers from the list. This can be useful when - // preparing registers for an AAPCS64 function call, for example. - void RemoveCalleeSaved(); - - CPURegister PopLowestIndex(); - CPURegister PopHighestIndex(); - - // AAPCS64 callee-saved registers. - static CPURegList GetCalleeSaved(unsigned size = kXRegSize); - static CPURegList GetCalleeSavedV(unsigned size = kDRegSize); - - // AAPCS64 caller-saved registers. Note that this includes lr. - // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top - // 64-bits being caller-saved. - static CPURegList GetCallerSaved(unsigned size = kXRegSize); - static CPURegList GetCallerSavedV(unsigned size = kDRegSize); - - bool IsEmpty() const { - VIXL_ASSERT(IsValid()); - return list_ == 0; - } - - bool IncludesAliasOf(const CPURegister& other) const { - VIXL_ASSERT(IsValid()); - return (type_ == other.type()) && ((other.Bit() & list_) != 0); - } - - bool IncludesAliasOf(int code) const { - VIXL_ASSERT(IsValid()); - return ((code & list_) != 0); - } - - int Count() const { - VIXL_ASSERT(IsValid()); - return CountSetBits(list_); - } - - unsigned RegisterSizeInBits() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - unsigned RegisterSizeInBytes() const { - int size_in_bits = RegisterSizeInBits(); - VIXL_ASSERT((size_in_bits % 8) == 0); - return size_in_bits / 8; - } - - unsigned TotalSizeInBytes() const { - VIXL_ASSERT(IsValid()); - return RegisterSizeInBytes() * Count(); - } - - private: - RegList list_; - unsigned size_; - CPURegister::RegisterType type_; - - bool IsValid() const; -}; - - -// AAPCS64 callee-saved registers. -extern const CPURegList kCalleeSaved; -extern const CPURegList kCalleeSavedV; - - -// AAPCS64 caller-saved registers. Note that this includes lr. -extern const CPURegList kCallerSaved; -extern const CPURegList kCallerSavedV; - - -// Operand. -class Operand { - public: - // # - // where is int64_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - Operand(int64_t immediate = 0); // NOLINT(runtime/explicit) - - // rm, { #} - // where is one of {LSL, LSR, ASR, ROR}. - // is uint6_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - Operand(Register reg, - Shift shift = LSL, - unsigned shift_amount = 0); // NOLINT(runtime/explicit) - - // rm, { {#}} - // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. - // is uint2_t. - explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); - - bool IsImmediate() const; - bool IsShiftedRegister() const; - bool IsExtendedRegister() const; - bool IsZero() const; - - // This returns an LSL shift (<= 4) operand as an equivalent extend operand, - // which helps in the encoding of instructions that use the stack pointer. - Operand ToExtendedRegister() const; - - int64_t immediate() const { - VIXL_ASSERT(IsImmediate()); - return immediate_; - } - - Register reg() const { - VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return reg_; - } - - Shift shift() const { - VIXL_ASSERT(IsShiftedRegister()); - return shift_; - } - - Extend extend() const { - VIXL_ASSERT(IsExtendedRegister()); - return extend_; - } - - unsigned shift_amount() const { - VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return shift_amount_; - } - - private: - int64_t immediate_; - Register reg_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -// MemOperand represents the addressing mode of a load or store instruction. -class MemOperand { - public: - explicit MemOperand(Register base, - int64_t offset = 0, - AddrMode addrmode = Offset); - MemOperand(Register base, - Register regoffset, - Shift shift = LSL, - unsigned shift_amount = 0); - MemOperand(Register base, - Register regoffset, - Extend extend, - unsigned shift_amount = 0); - MemOperand(Register base, - const Operand& offset, - AddrMode addrmode = Offset); - - const Register& base() const { return base_; } - const Register& regoffset() const { return regoffset_; } - int64_t offset() const { return offset_; } - AddrMode addrmode() const { return addrmode_; } - Shift shift() const { return shift_; } - Extend extend() const { return extend_; } - unsigned shift_amount() const { return shift_amount_; } - bool IsImmediateOffset() const; - bool IsRegisterOffset() const; - bool IsPreIndex() const; - bool IsPostIndex() const; - - void AddOffset(int64_t offset); - - private: - Register base_; - Register regoffset_; - int64_t offset_; - AddrMode addrmode_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -class LabelTestHelper; // Forward declaration. - - -class Label { - public: - Label() : location_(kLocationUnbound) {} - ~Label() { - // If the label has been linked to, it needs to be bound to a target. - VIXL_ASSERT(!IsLinked() || IsBound()); - } - - bool IsBound() const { return location_ >= 0; } - bool IsLinked() const { return !links_.empty(); } - - ptrdiff_t location() const { return location_; } - - static const int kNPreallocatedLinks = 4; - static const ptrdiff_t kInvalidLinkKey = PTRDIFF_MAX; - static const size_t kReclaimFrom = 512; - static const size_t kReclaimFactor = 2; - - typedef InvalSet LinksSetBase; - typedef InvalSetIterator LabelLinksIteratorBase; - - private: - class LinksSet : public LinksSetBase { - public: - LinksSet() : LinksSetBase() {} - }; - - // Allows iterating over the links of a label. The behaviour is undefined if - // the list of links is modified in any way while iterating. - class LabelLinksIterator : public LabelLinksIteratorBase { - public: - explicit LabelLinksIterator(Label* label) - : LabelLinksIteratorBase(&label->links_) {} - }; - - void Bind(ptrdiff_t location) { - // Labels can only be bound once. - VIXL_ASSERT(!IsBound()); - location_ = location; - } - - void AddLink(ptrdiff_t instruction) { - // If a label is bound, the assembler already has the information it needs - // to write the instruction, so there is no need to add it to links_. - VIXL_ASSERT(!IsBound()); - links_.insert(instruction); - } - - void DeleteLink(ptrdiff_t instruction) { - links_.erase(instruction); - } - - void ClearAllLinks() { - links_.clear(); - } - - // TODO: The comment below considers average case complexity for our - // usual use-cases. The elements of interest are: - // - Branches to a label are emitted in order: branch instructions to a label - // are generated at an offset in the code generation buffer greater than any - // other branch to that same label already generated. As an example, this can - // be broken when an instruction is patched to become a branch. Note that the - // code will still work, but the complexity considerations below may locally - // not apply any more. - // - Veneers are generated in order: for multiple branches of the same type - // branching to the same unbound label going out of range, veneers are - // generated in growing order of the branch instruction offset from the start - // of the buffer. - // - // When creating a veneer for a branch going out of range, the link for this - // branch needs to be removed from this `links_`. Since all branches are - // tracked in one underlying InvalSet, the complexity for this deletion is the - // same as for finding the element, ie. O(n), where n is the number of links - // in the set. - // This could be reduced to O(1) by using the same trick as used when tracking - // branch information for veneers: split the container to use one set per type - // of branch. With that setup, when a veneer is created and the link needs to - // be deleted, if the two points above hold, it must be the minimum element of - // the set for its type of branch, and that minimum element will be accessible - // in O(1). - - // The offsets of the instructions that have linked to this label. - LinksSet links_; - // The label location. - ptrdiff_t location_; - - static const ptrdiff_t kLocationUnbound = -1; - - // It is not safe to copy labels, so disable the copy constructor and operator - // by declaring them private (without an implementation). - Label(const Label&); - void operator=(const Label&); - - // The Assembler class is responsible for binding and linking labels, since - // the stored offsets need to be consistent with the Assembler's buffer. - friend class Assembler; - // The MacroAssembler and VeneerPool handle resolution of branches to distant - // targets. - friend class MacroAssembler; - friend class VeneerPool; -}; - - -// Required InvalSet template specialisations. -#define INVAL_SET_TEMPLATE_PARAMETERS \ - ptrdiff_t, \ - Label::kNPreallocatedLinks, \ - ptrdiff_t, \ - Label::kInvalidLinkKey, \ - Label::kReclaimFrom, \ - Label::kReclaimFactor -template<> -inline ptrdiff_t InvalSet::Key( - const ptrdiff_t& element) { - return element; -} -template<> -inline void InvalSet::SetKey( - ptrdiff_t* element, ptrdiff_t key) { - *element = key; -} -#undef INVAL_SET_TEMPLATE_PARAMETERS - - -class Assembler; -class LiteralPool; - -// A literal is a 32-bit or 64-bit piece of data stored in the instruction -// stream and loaded through a pc relative load. The same literal can be -// referred to by multiple instructions but a literal can only reside at one -// place in memory. A literal can be used by a load before or after being -// placed in memory. -// -// Internally an offset of 0 is associated with a literal which has been -// neither used nor placed. Then two possibilities arise: -// 1) the label is placed, the offset (stored as offset + 1) is used to -// resolve any subsequent load using the label. -// 2) the label is not placed and offset is the offset of the last load using -// the literal (stored as -offset -1). If multiple loads refer to this -// literal then the last load holds the offset of the preceding load and -// all loads form a chain. Once the offset is placed all the loads in the -// chain are resolved and future loads fall back to possibility 1. -class RawLiteral { - public: - enum DeletionPolicy { - kDeletedOnPlacementByPool, - kDeletedOnPoolDestruction, - kManuallyDeleted - }; - - RawLiteral(size_t size, - LiteralPool* literal_pool, - DeletionPolicy deletion_policy = kManuallyDeleted); - - // The literal pool only sees and deletes `RawLiteral*` pointers, but they are - // actually pointing to `Literal` objects. - virtual ~RawLiteral() {} - - size_t size() { - VIXL_STATIC_ASSERT(kDRegSizeInBytes == kXRegSizeInBytes); - VIXL_STATIC_ASSERT(kSRegSizeInBytes == kWRegSizeInBytes); - VIXL_ASSERT((size_ == kXRegSizeInBytes) || - (size_ == kWRegSizeInBytes) || - (size_ == kQRegSizeInBytes)); - return size_; - } - uint64_t raw_value128_low64() { - VIXL_ASSERT(size_ == kQRegSizeInBytes); - return low64_; - } - uint64_t raw_value128_high64() { - VIXL_ASSERT(size_ == kQRegSizeInBytes); - return high64_; - } - uint64_t raw_value64() { - VIXL_ASSERT(size_ == kXRegSizeInBytes); - VIXL_ASSERT(high64_ == 0); - return low64_; - } - uint32_t raw_value32() { - VIXL_ASSERT(size_ == kWRegSizeInBytes); - VIXL_ASSERT(high64_ == 0); - VIXL_ASSERT(is_uint32(low64_) || is_int32(low64_)); - return static_cast(low64_); - } - bool IsUsed() { return offset_ < 0; } - bool IsPlaced() { return offset_ > 0; } - - LiteralPool* GetLiteralPool() const { - return literal_pool_; - } - - ptrdiff_t offset() { - VIXL_ASSERT(IsPlaced()); - return offset_ - 1; - } - - protected: - void set_offset(ptrdiff_t offset) { - VIXL_ASSERT(offset >= 0); - VIXL_ASSERT(IsWordAligned(offset)); - VIXL_ASSERT(!IsPlaced()); - offset_ = offset + 1; - } - ptrdiff_t last_use() { - VIXL_ASSERT(IsUsed()); - return -offset_ - 1; - } - void set_last_use(ptrdiff_t offset) { - VIXL_ASSERT(offset >= 0); - VIXL_ASSERT(IsWordAligned(offset)); - VIXL_ASSERT(!IsPlaced()); - offset_ = -offset - 1; - } - - size_t size_; - ptrdiff_t offset_; - uint64_t low64_; - uint64_t high64_; - - private: - LiteralPool* literal_pool_; - DeletionPolicy deletion_policy_; - - friend class Assembler; - friend class LiteralPool; -}; - - -template -class Literal : public RawLiteral { - public: - explicit Literal(T value, - LiteralPool* literal_pool = NULL, - RawLiteral::DeletionPolicy ownership = kManuallyDeleted) - : RawLiteral(sizeof(value), literal_pool, ownership) { - VIXL_STATIC_ASSERT(sizeof(value) <= kXRegSizeInBytes); - UpdateValue(value); - } - - Literal(T high64, T low64, - LiteralPool* literal_pool = NULL, - RawLiteral::DeletionPolicy ownership = kManuallyDeleted) - : RawLiteral(kQRegSizeInBytes, literal_pool, ownership) { - VIXL_STATIC_ASSERT(sizeof(low64) == (kQRegSizeInBytes / 2)); - UpdateValue(high64, low64); - } - - virtual ~Literal() {} - - // Update the value of this literal, if necessary by rewriting the value in - // the pool. - // If the literal has already been placed in a literal pool, the address of - // the start of the code buffer must be provided, as the literal only knows it - // offset from there. This also allows patching the value after the code has - // been moved in memory. - void UpdateValue(T new_value, uint8_t* code_buffer = NULL) { - VIXL_ASSERT(sizeof(new_value) == size_); - memcpy(&low64_, &new_value, sizeof(new_value)); - if (IsPlaced()) { - VIXL_ASSERT(code_buffer != NULL); - RewriteValueInCode(code_buffer); - } - } - - void UpdateValue(T high64, T low64, uint8_t* code_buffer = NULL) { - VIXL_ASSERT(sizeof(low64) == size_ / 2); - memcpy(&low64_, &low64, sizeof(low64)); - memcpy(&high64_, &high64, sizeof(high64)); - if (IsPlaced()) { - VIXL_ASSERT(code_buffer != NULL); - RewriteValueInCode(code_buffer); - } - } - - void UpdateValue(T new_value, const Assembler* assembler); - void UpdateValue(T high64, T low64, const Assembler* assembler); - - private: - void RewriteValueInCode(uint8_t* code_buffer) { - VIXL_ASSERT(IsPlaced()); - VIXL_STATIC_ASSERT(sizeof(T) <= kXRegSizeInBytes); - switch (size()) { - case kSRegSizeInBytes: - *reinterpret_cast(code_buffer + offset()) = raw_value32(); - break; - case kDRegSizeInBytes: - *reinterpret_cast(code_buffer + offset()) = raw_value64(); - break; - default: - VIXL_ASSERT(size() == kQRegSizeInBytes); - uint64_t* base_address = - reinterpret_cast(code_buffer + offset()); - *base_address = raw_value128_low64(); - *(base_address + 1) = raw_value128_high64(); - } - } -}; - - -// Control whether or not position-independent code should be emitted. -enum PositionIndependentCodeOption { - // All code generated will be position-independent; all branches and - // references to labels generated with the Label class will use PC-relative - // addressing. - PositionIndependentCode, - - // Allow VIXL to generate code that refers to absolute addresses. With this - // option, it will not be possible to copy the code buffer and run it from a - // different address; code must be generated in its final location. - PositionDependentCode, - - // Allow VIXL to assume that the bottom 12 bits of the address will be - // constant, but that the top 48 bits may change. This allows `adrp` to - // function in systems which copy code between pages, but otherwise maintain - // 4KB page alignment. - PageOffsetDependentCode -}; - - -// Control how scaled- and unscaled-offset loads and stores are generated. -enum LoadStoreScalingOption { - // Prefer scaled-immediate-offset instructions, but emit unscaled-offset, - // register-offset, pre-index or post-index instructions if necessary. - PreferScaledOffset, - - // Prefer unscaled-immediate-offset instructions, but emit scaled-offset, - // register-offset, pre-index or post-index instructions if necessary. - PreferUnscaledOffset, - - // Require scaled-immediate-offset instructions. - RequireScaledOffset, - - // Require unscaled-immediate-offset instructions. - RequireUnscaledOffset -}; - - -// Assembler. -class Assembler { - public: - Assembler(size_t capacity, - PositionIndependentCodeOption pic = PositionIndependentCode); - Assembler(byte* buffer, size_t capacity, - PositionIndependentCodeOption pic = PositionIndependentCode); - - // The destructor asserts that one of the following is true: - // * The Assembler object has not been used. - // * Nothing has been emitted since the last Reset() call. - // * Nothing has been emitted since the last FinalizeCode() call. - ~Assembler(); - - // System functions. - - // Start generating code from the beginning of the buffer, discarding any code - // and data that has already been emitted into the buffer. - void Reset(); - - // Finalize a code buffer of generated instructions. This function must be - // called before executing or copying code from the buffer. - void FinalizeCode(); - - // Label. - // Bind a label to the current PC. - void bind(Label* label); - - // Bind a label to a specified offset from the start of the buffer. - void BindToOffset(Label* label, ptrdiff_t offset); - - // Place a literal at the current PC. - void place(RawLiteral* literal); - - ptrdiff_t CursorOffset() const { - return buffer_->CursorOffset(); - } - - ptrdiff_t BufferEndOffset() const { - return static_cast(buffer_->capacity()); - } - - // Return the address of an offset in the buffer. - template - T GetOffsetAddress(ptrdiff_t offset) const { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return buffer_->GetOffsetAddress(offset); - } - - // Return the address of a bound label. - template - T GetLabelAddress(const Label * label) const { - VIXL_ASSERT(label->IsBound()); - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(label->location()); - } - - // Return the address of the cursor. - template - T GetCursorAddress() const { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(CursorOffset()); - } - - // Return the address of the start of the buffer. - template - T GetStartAddress() const { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(0); - } - - Instruction* InstructionAt(ptrdiff_t instruction_offset) { - return GetOffsetAddress(instruction_offset); - } - - ptrdiff_t InstructionOffset(Instruction* instruction) { - VIXL_STATIC_ASSERT(sizeof(*instruction) == 1); - ptrdiff_t offset = instruction - GetStartAddress(); - VIXL_ASSERT((0 <= offset) && - (offset < static_cast(BufferCapacity()))); - return offset; - } - - // Instruction set functions. - - // Branch / Jump instructions. - // Branch to register. - void br(const Register& xn); - - // Branch with link to register. - void blr(const Register& xn); - - // Branch to register with return hint. - void ret(const Register& xn = lr); - - // Unconditional branch to label. - void b(Label* label); - - // Conditional branch to label. - void b(Label* label, Condition cond); - - // Unconditional branch to PC offset. - void b(int imm26); - - // Conditional branch to PC offset. - void b(int imm19, Condition cond); - - // Branch with link to label. - void bl(Label* label); - - // Branch with link to PC offset. - void bl(int imm26); - - // Compare and branch to label if zero. - void cbz(const Register& rt, Label* label); - - // Compare and branch to PC offset if zero. - void cbz(const Register& rt, int imm19); - - // Compare and branch to label if not zero. - void cbnz(const Register& rt, Label* label); - - // Compare and branch to PC offset if not zero. - void cbnz(const Register& rt, int imm19); - - // Table lookup from one register. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Table lookup from two registers. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vm); - - // Table lookup from three registers. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vm); - - // Table lookup from four registers. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vn4, - const VRegister& vm); - - // Table lookup extension from one register. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Table lookup extension from two registers. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vm); - - // Table lookup extension from three registers. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vm); - - // Table lookup extension from four registers. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vn4, - const VRegister& vm); - - // Test bit and branch to label if zero. - void tbz(const Register& rt, unsigned bit_pos, Label* label); - - // Test bit and branch to PC offset if zero. - void tbz(const Register& rt, unsigned bit_pos, int imm14); - - // Test bit and branch to label if not zero. - void tbnz(const Register& rt, unsigned bit_pos, Label* label); - - // Test bit and branch to PC offset if not zero. - void tbnz(const Register& rt, unsigned bit_pos, int imm14); - - // Address calculation instructions. - // Calculate a PC-relative address. Unlike for branches the offset in adr is - // unscaled (i.e. the result can be unaligned). - - // Calculate the address of a label. - void adr(const Register& rd, Label* label); - - // Calculate the address of a PC offset. - void adr(const Register& rd, int imm21); - - // Calculate the page address of a label. - void adrp(const Register& rd, Label* label); - - // Calculate the page address of a PC offset. - void adrp(const Register& rd, int imm21); - - // Data Processing instructions. - // Add. - void add(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add and update status flags. - void adds(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare negative. - void cmn(const Register& rn, const Operand& operand); - - // Subtract. - void sub(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract and update status flags. - void subs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare. - void cmp(const Register& rn, const Operand& operand); - - // Negate. - void neg(const Register& rd, - const Operand& operand); - - // Negate and update status flags. - void negs(const Register& rd, - const Operand& operand); - - // Add with carry bit. - void adc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add with carry bit and update status flags. - void adcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit. - void sbc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit and update status flags. - void sbcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Negate with carry bit. - void ngc(const Register& rd, - const Operand& operand); - - // Negate with carry bit and update status flags. - void ngcs(const Register& rd, - const Operand& operand); - - // Logical instructions. - // Bitwise and (A & B). - void and_(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise and (A & B) and update status flags. - void ands(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit test and set flags. - void tst(const Register& rn, const Operand& operand); - - // Bit clear (A & ~B). - void bic(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit clear (A & ~B) and update status flags. - void bics(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise or (A | B). - void orr(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise nor (A | ~B). - void orn(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise eor/xor (A ^ B). - void eor(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise enor/xnor (A ^ ~B). - void eon(const Register& rd, const Register& rn, const Operand& operand); - - // Logical shift left by variable. - void lslv(const Register& rd, const Register& rn, const Register& rm); - - // Logical shift right by variable. - void lsrv(const Register& rd, const Register& rn, const Register& rm); - - // Arithmetic shift right by variable. - void asrv(const Register& rd, const Register& rn, const Register& rm); - - // Rotate right by variable. - void rorv(const Register& rd, const Register& rn, const Register& rm); - - // Bitfield instructions. - // Bitfield move. - void bfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Signed bitfield move. - void sbfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Unsigned bitfield move. - void ubfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Bfm aliases. - // Bitfield insert. - void bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Bitfield extract and insert low. - void bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - bfm(rd, rn, lsb, lsb + width - 1); - } - - // Sbfm aliases. - // Arithmetic shift right. - void asr(const Register& rd, const Register& rn, unsigned shift) { - VIXL_ASSERT(shift < rd.size()); - sbfm(rd, rn, shift, rd.size() - 1); - } - - // Signed bitfield insert with zero at right. - void sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Signed bitfield extract. - void sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - sbfm(rd, rn, lsb, lsb + width - 1); - } - - // Signed extend byte. - void sxtb(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 7); - } - - // Signed extend halfword. - void sxth(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 15); - } - - // Signed extend word. - void sxtw(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 31); - } - - // Ubfm aliases. - // Logical shift left. - void lsl(const Register& rd, const Register& rn, unsigned shift) { - unsigned reg_size = rd.size(); - VIXL_ASSERT(shift < reg_size); - ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); - } - - // Logical shift right. - void lsr(const Register& rd, const Register& rn, unsigned shift) { - VIXL_ASSERT(shift < rd.size()); - ubfm(rd, rn, shift, rd.size() - 1); - } - - // Unsigned bitfield insert with zero at right. - void ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Unsigned bitfield extract. - void ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - ubfm(rd, rn, lsb, lsb + width - 1); - } - - // Unsigned extend byte. - void uxtb(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 7); - } - - // Unsigned extend halfword. - void uxth(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 15); - } - - // Unsigned extend word. - void uxtw(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 31); - } - - // Extract. - void extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb); - - // Conditional select: rd = cond ? rn : rm. - void csel(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select increment: rd = cond ? rn : rm + 1. - void csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select inversion: rd = cond ? rn : ~rm. - void csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select negation: rd = cond ? rn : -rm. - void csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional set: rd = cond ? 1 : 0. - void cset(const Register& rd, Condition cond); - - // Conditional set mask: rd = cond ? -1 : 0. - void csetm(const Register& rd, Condition cond); - - // Conditional increment: rd = cond ? rn + 1 : rn. - void cinc(const Register& rd, const Register& rn, Condition cond); - - // Conditional invert: rd = cond ? ~rn : rn. - void cinv(const Register& rd, const Register& rn, Condition cond); - - // Conditional negate: rd = cond ? -rn : rn. - void cneg(const Register& rd, const Register& rn, Condition cond); - - // Rotate right. - void ror(const Register& rd, const Register& rs, unsigned shift) { - extr(rd, rs, rs, shift); - } - - // Conditional comparison. - // Conditional compare negative. - void ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // Conditional compare. - void ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // CRC-32 checksum from byte. - void crc32b(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 checksum from half-word. - void crc32h(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 checksum from word. - void crc32w(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 checksum from double word. - void crc32x(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 C checksum from byte. - void crc32cb(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 C checksum from half-word. - void crc32ch(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 C checksum from word. - void crc32cw(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32C checksum from double word. - void crc32cx(const Register& rd, - const Register& rn, - const Register& rm); - - // Multiply. - void mul(const Register& rd, const Register& rn, const Register& rm); - - // Negated multiply. - void mneg(const Register& rd, const Register& rn, const Register& rm); - - // Signed long multiply: 32 x 32 -> 64-bit. - void smull(const Register& rd, const Register& rn, const Register& rm); - - // Signed multiply high: 64 x 64 -> 64-bit <127:64>. - void smulh(const Register& xd, const Register& xn, const Register& xm); - - // Multiply and accumulate. - void madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Multiply and subtract. - void msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. - void smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. - void umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply: 32 x 32 -> 64-bit. - void umull(const Register& rd, - const Register& rn, - const Register& rm) { - umaddl(rd, rn, rm, xzr); - } - - // Unsigned multiply high: 64 x 64 -> 64-bit <127:64>. - void umulh(const Register& xd, - const Register& xn, - const Register& xm); - - // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. - void smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. - void umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed integer divide. - void sdiv(const Register& rd, const Register& rn, const Register& rm); - - // Unsigned integer divide. - void udiv(const Register& rd, const Register& rn, const Register& rm); - - // Bit reverse. - void rbit(const Register& rd, const Register& rn); - - // Reverse bytes in 16-bit half words. - void rev16(const Register& rd, const Register& rn); - - // Reverse bytes in 32-bit words. - void rev32(const Register& rd, const Register& rn); - - // Reverse bytes. - void rev(const Register& rd, const Register& rn); - - // Count leading zeroes. - void clz(const Register& rd, const Register& rn); - - // Count leading sign bits. - void cls(const Register& rd, const Register& rn); - - // Memory instructions. - // Load integer or FP register. - void ldr(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store integer or FP register. - void str(const CPURegister& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load word with sign extension. - void ldrsw(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load byte. - void ldrb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store byte. - void strb(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load byte with sign extension. - void ldrsb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load half-word. - void ldrh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store half-word. - void strh(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load half-word with sign extension. - void ldrsh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load integer or FP register (with unscaled offset). - void ldur(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store integer or FP register (with unscaled offset). - void stur(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load word with sign extension. - void ldursw(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load byte (with unscaled offset). - void ldurb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store byte (with unscaled offset). - void sturb(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load byte with sign extension (and unscaled offset). - void ldursb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load half-word (with unscaled offset). - void ldurh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store half-word (with unscaled offset). - void sturh(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load half-word with sign extension (and unscaled offset). - void ldursh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load integer or FP register pair. - void ldp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair. - void stp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load word pair with sign extension. - void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); - - // Load integer or FP register pair, non-temporal. - void ldnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair, non-temporal. - void stnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load integer or FP register from literal pool. - void ldr(const CPURegister& rt, RawLiteral* literal); - - // Load word with sign extension from literal pool. - void ldrsw(const Register& rt, RawLiteral* literal); - - // Load integer or FP register from pc + imm19 << 2. - void ldr(const CPURegister& rt, int imm19); - - // Load word with sign extension from pc + imm19 << 2. - void ldrsw(const Register& rt, int imm19); - - // Store exclusive byte. - void stxrb(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store exclusive half-word. - void stxrh(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store exclusive register. - void stxr(const Register& rs, const Register& rt, const MemOperand& dst); - - // Load exclusive byte. - void ldxrb(const Register& rt, const MemOperand& src); - - // Load exclusive half-word. - void ldxrh(const Register& rt, const MemOperand& src); - - // Load exclusive register. - void ldxr(const Register& rt, const MemOperand& src); - - // Store exclusive register pair. - void stxp(const Register& rs, - const Register& rt, - const Register& rt2, - const MemOperand& dst); - - // Load exclusive register pair. - void ldxp(const Register& rt, const Register& rt2, const MemOperand& src); - - // Store-release exclusive byte. - void stlxrb(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store-release exclusive half-word. - void stlxrh(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store-release exclusive register. - void stlxr(const Register& rs, const Register& rt, const MemOperand& dst); - - // Load-acquire exclusive byte. - void ldaxrb(const Register& rt, const MemOperand& src); - - // Load-acquire exclusive half-word. - void ldaxrh(const Register& rt, const MemOperand& src); - - // Load-acquire exclusive register. - void ldaxr(const Register& rt, const MemOperand& src); - - // Store-release exclusive register pair. - void stlxp(const Register& rs, - const Register& rt, - const Register& rt2, - const MemOperand& dst); - - // Load-acquire exclusive register pair. - void ldaxp(const Register& rt, const Register& rt2, const MemOperand& src); - - // Store-release byte. - void stlrb(const Register& rt, const MemOperand& dst); - - // Store-release half-word. - void stlrh(const Register& rt, const MemOperand& dst); - - // Store-release register. - void stlr(const Register& rt, const MemOperand& dst); - - // Load-acquire byte. - void ldarb(const Register& rt, const MemOperand& src); - - // Load-acquire half-word. - void ldarh(const Register& rt, const MemOperand& src); - - // Load-acquire register. - void ldar(const Register& rt, const MemOperand& src); - - // Prefetch memory. - void prfm(PrefetchOperation op, const MemOperand& addr, - LoadStoreScalingOption option = PreferScaledOffset); - - // Prefetch memory (with unscaled offset). - void prfum(PrefetchOperation op, const MemOperand& addr, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Prefetch memory in the literal pool. - void prfm(PrefetchOperation op, RawLiteral* literal); - - // Prefetch from pc + imm19 << 2. - void prfm(PrefetchOperation op, int imm19); - - // Move instructions. The default shift of -1 indicates that the move - // instruction will calculate an appropriate 16-bit immediate and left shift - // that is equal to the 64-bit immediate argument. If an explicit left shift - // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. - // - // For movk, an explicit shift can be used to indicate which half word should - // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant - // half word with zero, whereas movk(x0, 0, 48) will overwrite the - // most-significant. - - // Move immediate and keep. - void movk(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVK); - } - - // Move inverted immediate. - void movn(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVN); - } - - // Move immediate. - void movz(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVZ); - } - - // Misc instructions. - // Monitor debug-mode breakpoint. - void brk(int code); - - // Halting debug-mode breakpoint. - void hlt(int code); - - // Generate exception targeting EL1. - void svc(int code); - - // Move register to register. - void mov(const Register& rd, const Register& rn); - - // Move inverted operand to register. - void mvn(const Register& rd, const Operand& operand); - - // System instructions. - // Move to register from system register. - void mrs(const Register& rt, SystemRegister sysreg); - - // Move from register to system register. - void msr(SystemRegister sysreg, const Register& rt); - - // System instruction. - void sys(int op1, int crn, int crm, int op2, const Register& rt = xzr); - - // System instruction with pre-encoded op (op1:crn:crm:op2). - void sys(int op, const Register& rt = xzr); - - // System data cache operation. - void dc(DataCacheOp op, const Register& rt); - - // System instruction cache operation. - void ic(InstructionCacheOp op, const Register& rt); - - // System hint. - void hint(SystemHint code); - - // Clear exclusive monitor. - void clrex(int imm4 = 0xf); - - // Data memory barrier. - void dmb(BarrierDomain domain, BarrierType type); - - // Data synchronization barrier. - void dsb(BarrierDomain domain, BarrierType type); - - // Instruction synchronization barrier. - void isb(); - - // Alias for system instructions. - // No-op. - void nop() { - hint(NOP); - } - - // FP and NEON instructions. - // Move double precision immediate to FP register. - void fmov(const VRegister& vd, double imm); - - // Move single precision immediate to FP register. - void fmov(const VRegister& vd, float imm); - - // Move FP register to register. - void fmov(const Register& rd, const VRegister& fn); - - // Move register to FP register. - void fmov(const VRegister& vd, const Register& rn); - - // Move FP register to FP register. - void fmov(const VRegister& vd, const VRegister& fn); - - // Move 64-bit register to top half of 128-bit FP register. - void fmov(const VRegister& vd, int index, const Register& rn); - - // Move top half of 128-bit FP register to 64-bit register. - void fmov(const Register& rd, const VRegister& vn, int index); - - // FP add. - void fadd(const VRegister& vd, const VRegister& vn, const VRegister& vm); - - // FP subtract. - void fsub(const VRegister& vd, const VRegister& vn, const VRegister& vm); - - // FP multiply. - void fmul(const VRegister& vd, const VRegister& vn, const VRegister& vm); - - // FP fused multiply-add. - void fmadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP fused multiply-subtract. - void fmsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP fused multiply-add and negate. - void fnmadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP fused multiply-subtract and negate. - void fnmsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP multiply-negate scalar. - void fnmul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP reciprocal exponent scalar. - void frecpx(const VRegister& vd, - const VRegister& vn); - - // FP divide. - void fdiv(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP maximum. - void fmax(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP minimum. - void fmin(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP maximum number. - void fmaxnm(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP minimum number. - void fminnm(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP absolute. - void fabs(const VRegister& vd, const VRegister& vn); - - // FP negate. - void fneg(const VRegister& vd, const VRegister& vn); - - // FP square root. - void fsqrt(const VRegister& vd, const VRegister& vn); - - // FP round to integer, nearest with ties to away. - void frinta(const VRegister& vd, const VRegister& vn); - - // FP round to integer, implicit rounding. - void frinti(const VRegister& vd, const VRegister& vn); - - // FP round to integer, toward minus infinity. - void frintm(const VRegister& vd, const VRegister& vn); - - // FP round to integer, nearest with ties to even. - void frintn(const VRegister& vd, const VRegister& vn); - - // FP round to integer, toward plus infinity. - void frintp(const VRegister& vd, const VRegister& vn); - - // FP round to integer, exact, implicit rounding. - void frintx(const VRegister& vd, const VRegister& vn); - - // FP round to integer, towards zero. - void frintz(const VRegister& vd, const VRegister& vn); - - void FPCompareMacro(const VRegister& vn, - double value, - FPTrapFlags trap); - - void FPCompareMacro(const VRegister& vn, - const VRegister& vm, - FPTrapFlags trap); - - // FP compare registers. - void fcmp(const VRegister& vn, const VRegister& vm); - - // FP compare immediate. - void fcmp(const VRegister& vn, double value); - - void FPCCompareMacro(const VRegister& vn, - const VRegister& vm, - StatusFlags nzcv, - Condition cond, - FPTrapFlags trap); - - // FP conditional compare. - void fccmp(const VRegister& vn, - const VRegister& vm, - StatusFlags nzcv, - Condition cond); - - // FP signaling compare registers. - void fcmpe(const VRegister& vn, const VRegister& vm); - - // FP signaling compare immediate. - void fcmpe(const VRegister& vn, double value); - - // FP conditional signaling compare. - void fccmpe(const VRegister& vn, - const VRegister& vm, - StatusFlags nzcv, - Condition cond); - - // FP conditional select. - void fcsel(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - Condition cond); - - // Common FP Convert functions. - void NEONFPConvertToInt(const Register& rd, - const VRegister& vn, - Instr op); - void NEONFPConvertToInt(const VRegister& vd, - const VRegister& vn, - Instr op); - - // FP convert between precisions. - void fcvt(const VRegister& vd, const VRegister& vn); - - // FP convert to higher precision. - void fcvtl(const VRegister& vd, const VRegister& vn); - - // FP convert to higher precision (second part). - void fcvtl2(const VRegister& vd, const VRegister& vn); - - // FP convert to lower precision. - void fcvtn(const VRegister& vd, const VRegister& vn); - - // FP convert to lower prevision (second part). - void fcvtn2(const VRegister& vd, const VRegister& vn); - - // FP convert to lower precision, rounding to odd. - void fcvtxn(const VRegister& vd, const VRegister& vn); - - // FP convert to lower precision, rounding to odd (second part). - void fcvtxn2(const VRegister& vd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to away. - void fcvtas(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to away. - void fcvtau(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to away. - void fcvtas(const VRegister& vd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to away. - void fcvtau(const VRegister& vd, const VRegister& vn); - - // FP convert to signed integer, round towards -infinity. - void fcvtms(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, round towards -infinity. - void fcvtmu(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, round towards -infinity. - void fcvtms(const VRegister& vd, const VRegister& vn); - - // FP convert to unsigned integer, round towards -infinity. - void fcvtmu(const VRegister& vd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to even. - void fcvtns(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to even. - void fcvtnu(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to even. - void fcvtns(const VRegister& rd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to even. - void fcvtnu(const VRegister& rd, const VRegister& vn); - - // FP convert to signed integer or fixed-point, round towards zero. - void fcvtzs(const Register& rd, const VRegister& vn, int fbits = 0); - - // FP convert to unsigned integer or fixed-point, round towards zero. - void fcvtzu(const Register& rd, const VRegister& vn, int fbits = 0); - - // FP convert to signed integer or fixed-point, round towards zero. - void fcvtzs(const VRegister& vd, const VRegister& vn, int fbits = 0); - - // FP convert to unsigned integer or fixed-point, round towards zero. - void fcvtzu(const VRegister& vd, const VRegister& vn, int fbits = 0); - - // FP convert to signed integer, round towards +infinity. - void fcvtps(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, round towards +infinity. - void fcvtpu(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, round towards +infinity. - void fcvtps(const VRegister& vd, const VRegister& vn); - - // FP convert to unsigned integer, round towards +infinity. - void fcvtpu(const VRegister& vd, const VRegister& vn); - - // Convert signed integer or fixed point to FP. - void scvtf(const VRegister& fd, const Register& rn, int fbits = 0); - - // Convert unsigned integer or fixed point to FP. - void ucvtf(const VRegister& fd, const Register& rn, int fbits = 0); - - // Convert signed integer or fixed-point to FP. - void scvtf(const VRegister& fd, const VRegister& vn, int fbits = 0); - - // Convert unsigned integer or fixed-point to FP. - void ucvtf(const VRegister& fd, const VRegister& vn, int fbits = 0); - - // Unsigned absolute difference. - void uabd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference. - void sabd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference and accumulate. - void uaba(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference and accumulate. - void saba(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add. - void add(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Subtract. - void sub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned halving add. - void uhadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed halving add. - void shadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned rounding halving add. - void urhadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed rounding halving add. - void srhadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned halving sub. - void uhsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed halving sub. - void shsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating add. - void uqadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating add. - void sqadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating subtract. - void uqsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating subtract. - void sqsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add pairwise. - void addp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add pair of elements scalar. - void addp(const VRegister& vd, - const VRegister& vn); - - // Multiply-add to accumulator. - void mla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Multiply-subtract to accumulator. - void mls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Multiply. - void mul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Multiply by scalar element. - void mul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Multiply-add by scalar element. - void mla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Multiply-subtract by scalar element. - void mls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-add by scalar element. - void smlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-add by scalar element (second part). - void smlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-add by scalar element. - void umlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-add by scalar element (second part). - void umlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-sub by scalar element. - void smlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-sub by scalar element (second part). - void smlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-sub by scalar element. - void umlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-sub by scalar element (second part). - void umlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply by scalar element. - void smull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply by scalar element (second part). - void smull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply by scalar element. - void umull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply by scalar element (second part). - void umull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating double long multiply by element. - void sqdmull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating double long multiply by element (second part). - void sqdmull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-add by element. - void sqdmlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-add by element (second part). - void sqdmlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-sub by element. - void sqdmlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-sub by element (second part). - void sqdmlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Compare equal. - void cmeq(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare signed greater than or equal. - void cmge(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare signed greater than. - void cmgt(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare unsigned higher. - void cmhi(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare unsigned higher or same. - void cmhs(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare bitwise test bits nonzero. - void cmtst(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare bitwise to zero. - void cmeq(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed greater than or equal to zero. - void cmge(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed greater than zero. - void cmgt(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed less than or equal to zero. - void cmle(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed less than zero. - void cmlt(const VRegister& vd, - const VRegister& vn, - int value); - - // Signed shift left by register. - void sshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned shift left by register. - void ushl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating shift left by register. - void sqshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating shift left by register. - void uqshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed rounding shift left by register. - void srshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned rounding shift left by register. - void urshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating rounding shift left by register. - void sqrshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating rounding shift left by register. - void uqrshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise and. - void and_(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise or. - void orr(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise or immediate. - void orr(const VRegister& vd, - const int imm8, - const int left_shift = 0); - - // Move register to register. - void mov(const VRegister& vd, - const VRegister& vn); - - // Bitwise orn. - void orn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise eor. - void eor(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bit clear immediate. - void bic(const VRegister& vd, - const int imm8, - const int left_shift = 0); - - // Bit clear. - void bic(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise insert if false. - void bif(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise insert if true. - void bit(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise select. - void bsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Polynomial multiply. - void pmul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Vector move immediate. - void movi(const VRegister& vd, - const uint64_t imm, - Shift shift = LSL, - const int shift_amount = 0); - - // Bitwise not. - void mvn(const VRegister& vd, - const VRegister& vn); - - // Vector move inverted immediate. - void mvni(const VRegister& vd, - const int imm8, - Shift shift = LSL, - const int shift_amount = 0); - - // Signed saturating accumulate of unsigned value. - void suqadd(const VRegister& vd, - const VRegister& vn); - - // Unsigned saturating accumulate of signed value. - void usqadd(const VRegister& vd, - const VRegister& vn); - - // Absolute value. - void abs(const VRegister& vd, - const VRegister& vn); - - // Signed saturating absolute value. - void sqabs(const VRegister& vd, - const VRegister& vn); - - // Negate. - void neg(const VRegister& vd, - const VRegister& vn); - - // Signed saturating negate. - void sqneg(const VRegister& vd, - const VRegister& vn); - - // Bitwise not. - void not_(const VRegister& vd, - const VRegister& vn); - - // Extract narrow. - void xtn(const VRegister& vd, - const VRegister& vn); - - // Extract narrow (second part). - void xtn2(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract narrow. - void sqxtn(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract narrow (second part). - void sqxtn2(const VRegister& vd, - const VRegister& vn); - - // Unsigned saturating extract narrow. - void uqxtn(const VRegister& vd, - const VRegister& vn); - - // Unsigned saturating extract narrow (second part). - void uqxtn2(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract unsigned narrow. - void sqxtun(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract unsigned narrow (second part). - void sqxtun2(const VRegister& vd, - const VRegister& vn); - - // Extract vector from pair of vectors. - void ext(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int index); - - // Duplicate vector element to vector or scalar. - void dup(const VRegister& vd, - const VRegister& vn, - int vn_index); - - // Move vector element to scalar. - void mov(const VRegister& vd, - const VRegister& vn, - int vn_index); - - // Duplicate general-purpose register to vector. - void dup(const VRegister& vd, - const Register& rn); - - // Insert vector element from another vector element. - void ins(const VRegister& vd, - int vd_index, - const VRegister& vn, - int vn_index); - - // Move vector element to another vector element. - void mov(const VRegister& vd, - int vd_index, - const VRegister& vn, - int vn_index); - - // Insert vector element from general-purpose register. - void ins(const VRegister& vd, - int vd_index, - const Register& rn); - - // Move general-purpose register to a vector element. - void mov(const VRegister& vd, - int vd_index, - const Register& rn); - - // Unsigned move vector element to general-purpose register. - void umov(const Register& rd, - const VRegister& vn, - int vn_index); - - // Move vector element to general-purpose register. - void mov(const Register& rd, - const VRegister& vn, - int vn_index); - - // Signed move vector element to general-purpose register. - void smov(const Register& rd, - const VRegister& vn, - int vn_index); - - // One-element structure load to one register. - void ld1(const VRegister& vt, - const MemOperand& src); - - // One-element structure load to two registers. - void ld1(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // One-element structure load to three registers. - void ld1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // One-element structure load to four registers. - void ld1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // One-element single structure load to one lane. - void ld1(const VRegister& vt, - int lane, - const MemOperand& src); - - // One-element single structure load to all lanes. - void ld1r(const VRegister& vt, - const MemOperand& src); - - // Two-element structure load. - void ld2(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // Two-element single structure load to one lane. - void ld2(const VRegister& vt, - const VRegister& vt2, - int lane, - const MemOperand& src); - - // Two-element single structure load to all lanes. - void ld2r(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // Three-element structure load. - void ld3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // Three-element single structure load to one lane. - void ld3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - int lane, - const MemOperand& src); - - // Three-element single structure load to all lanes. - void ld3r(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // Four-element structure load. - void ld4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // Four-element single structure load to one lane. - void ld4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - int lane, - const MemOperand& src); - - // Four-element single structure load to all lanes. - void ld4r(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // Count leading sign bits. - void cls(const VRegister& vd, - const VRegister& vn); - - // Count leading zero bits (vector). - void clz(const VRegister& vd, - const VRegister& vn); - - // Population count per byte. - void cnt(const VRegister& vd, - const VRegister& vn); - - // Reverse bit order. - void rbit(const VRegister& vd, - const VRegister& vn); - - // Reverse elements in 16-bit halfwords. - void rev16(const VRegister& vd, - const VRegister& vn); - - // Reverse elements in 32-bit words. - void rev32(const VRegister& vd, - const VRegister& vn); - - // Reverse elements in 64-bit doublewords. - void rev64(const VRegister& vd, - const VRegister& vn); - - // Unsigned reciprocal square root estimate. - void ursqrte(const VRegister& vd, - const VRegister& vn); - - // Unsigned reciprocal estimate. - void urecpe(const VRegister& vd, - const VRegister& vn); - - // Signed pairwise long add. - void saddlp(const VRegister& vd, - const VRegister& vn); - - // Unsigned pairwise long add. - void uaddlp(const VRegister& vd, - const VRegister& vn); - - // Signed pairwise long add and accumulate. - void sadalp(const VRegister& vd, - const VRegister& vn); - - // Unsigned pairwise long add and accumulate. - void uadalp(const VRegister& vd, - const VRegister& vn); - - // Shift left by immediate. - void shl(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift left by immediate. - void sqshl(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift left unsigned by immediate. - void sqshlu(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating shift left by immediate. - void uqshl(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed shift left long by immediate. - void sshll(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed shift left long by immediate (second part). - void sshll2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed extend long. - void sxtl(const VRegister& vd, - const VRegister& vn); - - // Signed extend long (second part). - void sxtl2(const VRegister& vd, - const VRegister& vn); - - // Unsigned shift left long by immediate. - void ushll(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned shift left long by immediate (second part). - void ushll2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift left long by element size. - void shll(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift left long by element size (second part). - void shll2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned extend long. - void uxtl(const VRegister& vd, - const VRegister& vn); - - // Unsigned extend long (second part). - void uxtl2(const VRegister& vd, - const VRegister& vn); - - // Shift left by immediate and insert. - void sli(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift right by immediate and insert. - void sri(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed maximum. - void smax(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed pairwise maximum. - void smaxp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add across vector. - void addv(const VRegister& vd, - const VRegister& vn); - - // Signed add long across vector. - void saddlv(const VRegister& vd, - const VRegister& vn); - - // Unsigned add long across vector. - void uaddlv(const VRegister& vd, - const VRegister& vn); - - // FP maximum number across vector. - void fmaxnmv(const VRegister& vd, - const VRegister& vn); - - // FP maximum across vector. - void fmaxv(const VRegister& vd, - const VRegister& vn); - - // FP minimum number across vector. - void fminnmv(const VRegister& vd, - const VRegister& vn); - - // FP minimum across vector. - void fminv(const VRegister& vd, - const VRegister& vn); - - // Signed maximum across vector. - void smaxv(const VRegister& vd, - const VRegister& vn); - - // Signed minimum. - void smin(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed minimum pairwise. - void sminp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed minimum across vector. - void sminv(const VRegister& vd, - const VRegister& vn); - - // One-element structure store from one register. - void st1(const VRegister& vt, - const MemOperand& src); - - // One-element structure store from two registers. - void st1(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // One-element structure store from three registers. - void st1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // One-element structure store from four registers. - void st1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // One-element single structure store from one lane. - void st1(const VRegister& vt, - int lane, - const MemOperand& src); - - // Two-element structure store from two registers. - void st2(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // Two-element single structure store from two lanes. - void st2(const VRegister& vt, - const VRegister& vt2, - int lane, - const MemOperand& src); - - // Three-element structure store from three registers. - void st3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // Three-element single structure store from three lanes. - void st3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - int lane, - const MemOperand& src); - - // Four-element structure store from four registers. - void st4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // Four-element single structure store from four lanes. - void st4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - int lane, - const MemOperand& src); - - // Unsigned add long. - void uaddl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned add long (second part). - void uaddl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned add wide. - void uaddw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned add wide (second part). - void uaddw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add long. - void saddl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add long (second part). - void saddl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add wide. - void saddw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add wide (second part). - void saddw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract long. - void usubl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract long (second part). - void usubl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract wide. - void usubw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract wide (second part). - void usubw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed subtract long. - void ssubl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed subtract long (second part). - void ssubl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed integer subtract wide. - void ssubw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed integer subtract wide (second part). - void ssubw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned maximum. - void umax(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned pairwise maximum. - void umaxp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned maximum across vector. - void umaxv(const VRegister& vd, - const VRegister& vn); - - // Unsigned minimum. - void umin(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned pairwise minimum. - void uminp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned minimum across vector. - void uminv(const VRegister& vd, - const VRegister& vn); - - // Transpose vectors (primary). - void trn1(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Transpose vectors (secondary). - void trn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unzip vectors (primary). - void uzp1(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unzip vectors (secondary). - void uzp2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Zip vectors (primary). - void zip1(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Zip vectors (secondary). - void zip2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed shift right by immediate. - void sshr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned shift right by immediate. - void ushr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed rounding shift right by immediate. - void srshr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned rounding shift right by immediate. - void urshr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed shift right by immediate and accumulate. - void ssra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned shift right by immediate and accumulate. - void usra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed rounding shift right by immediate and accumulate. - void srsra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned rounding shift right by immediate and accumulate. - void ursra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift right narrow by immediate. - void shrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift right narrow by immediate (second part). - void shrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Rounding shift right narrow by immediate. - void rshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Rounding shift right narrow by immediate (second part). - void rshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating shift right narrow by immediate. - void uqshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating shift right narrow by immediate (second part). - void uqshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating rounding shift right narrow by immediate. - void uqrshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating rounding shift right narrow by immediate (second part). - void uqrshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right narrow by immediate. - void sqshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right narrow by immediate (second part). - void sqshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating rounded shift right narrow by immediate. - void sqrshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating rounded shift right narrow by immediate (second part). - void sqrshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right unsigned narrow by immediate. - void sqshrun(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right unsigned narrow by immediate (second part). - void sqshrun2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed sat rounded shift right unsigned narrow by immediate. - void sqrshrun(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed sat rounded shift right unsigned narrow by immediate (second part). - void sqrshrun2(const VRegister& vd, - const VRegister& vn, - int shift); - - // FP reciprocal step. - void frecps(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP reciprocal estimate. - void frecpe(const VRegister& vd, - const VRegister& vn); - - // FP reciprocal square root estimate. - void frsqrte(const VRegister& vd, - const VRegister& vn); - - // FP reciprocal square root step. - void frsqrts(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference and accumulate long. - void sabal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference and accumulate long (second part). - void sabal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference and accumulate long. - void uabal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference and accumulate long (second part). - void uabal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference long. - void sabdl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference long (second part). - void sabdl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference long. - void uabdl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference long (second part). - void uabdl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Polynomial multiply long. - void pmull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Polynomial multiply long (second part). - void pmull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-add. - void smlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-add (second part). - void smlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-add. - void umlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-add (second part). - void umlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-sub. - void smlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-sub (second part). - void smlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-sub. - void umlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-sub (second part). - void umlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply. - void smull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply (second part). - void smull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-add. - void sqdmlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-add (second part). - void sqdmlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-subtract. - void sqdmlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-subtract (second part). - void sqdmlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply. - void sqdmull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply (second part). - void sqdmull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling multiply returning high half. - void sqdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating rounding doubling multiply returning high half. - void sqrdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling multiply element returning high half. - void sqdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating rounding doubling multiply element returning high half. - void sqrdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply long. - void umull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply (second part). - void umull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add narrow returning high half. - void addhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add narrow returning high half (second part). - void addhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding add narrow returning high half. - void raddhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding add narrow returning high half (second part). - void raddhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Subtract narrow returning high half. - void subhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Subtract narrow returning high half (second part). - void subhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding subtract narrow returning high half. - void rsubhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding subtract narrow returning high half (second part). - void rsubhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP vector multiply accumulate. - void fmla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP vector multiply subtract. - void fmls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP vector multiply extended. - void fmulx(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP absolute greater than or equal. - void facge(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP absolute greater than. - void facgt(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP multiply by element. - void fmul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP fused multiply-add to accumulator by element. - void fmla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP fused multiply-sub from accumulator by element. - void fmls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP multiply extended by element. - void fmulx(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP compare equal. - void fcmeq(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP greater than. - void fcmgt(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP greater than or equal. - void fcmge(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP compare equal to zero. - void fcmeq(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP greater than zero. - void fcmgt(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP greater than or equal to zero. - void fcmge(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP less than or equal to zero. - void fcmle(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP less than to zero. - void fcmlt(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP absolute difference. - void fabd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise add vector. - void faddp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise add scalar. - void faddp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise maximum vector. - void fmaxp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise maximum scalar. - void fmaxp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise minimum vector. - void fminp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise minimum scalar. - void fminp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise maximum number vector. - void fmaxnmp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise maximum number scalar. - void fmaxnmp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise minimum number vector. - void fminnmp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise minimum number scalar. - void fminnmp(const VRegister& vd, - const VRegister& vn); - - // Emit generic instructions. - // Emit raw instructions into the instruction stream. - void dci(Instr raw_inst) { Emit(raw_inst); } - - // Emit 32 bits of data into the instruction stream. - void dc32(uint32_t data) { - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit32(data); - } - - // Emit 64 bits of data into the instruction stream. - void dc64(uint64_t data) { - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit64(data); - } - - // Copy a string into the instruction stream, including the terminating NULL - // character. The instruction pointer is then aligned correctly for - // subsequent instructions. - void EmitString(const char * string) { - VIXL_ASSERT(string != NULL); - VIXL_ASSERT(buffer_monitor_ > 0); - - buffer_->EmitString(string); - buffer_->Align(); - } - - // Code generation helpers. - - // Register encoding. - static Instr Rd(CPURegister rd) { - VIXL_ASSERT(rd.code() != kSPRegInternalCode); - return rd.code() << Rd_offset; - } - - static Instr Rn(CPURegister rn) { - VIXL_ASSERT(rn.code() != kSPRegInternalCode); - return rn.code() << Rn_offset; - } - - static Instr Rm(CPURegister rm) { - VIXL_ASSERT(rm.code() != kSPRegInternalCode); - return rm.code() << Rm_offset; - } - - static Instr RmNot31(CPURegister rm) { - VIXL_ASSERT(rm.code() != kSPRegInternalCode); - VIXL_ASSERT(!rm.IsZero()); - return Rm(rm); - } - - static Instr Ra(CPURegister ra) { - VIXL_ASSERT(ra.code() != kSPRegInternalCode); - return ra.code() << Ra_offset; - } - - static Instr Rt(CPURegister rt) { - VIXL_ASSERT(rt.code() != kSPRegInternalCode); - return rt.code() << Rt_offset; - } - - static Instr Rt2(CPURegister rt2) { - VIXL_ASSERT(rt2.code() != kSPRegInternalCode); - return rt2.code() << Rt2_offset; - } - - static Instr Rs(CPURegister rs) { - VIXL_ASSERT(rs.code() != kSPRegInternalCode); - return rs.code() << Rs_offset; - } - - // These encoding functions allow the stack pointer to be encoded, and - // disallow the zero register. - static Instr RdSP(Register rd) { - VIXL_ASSERT(!rd.IsZero()); - return (rd.code() & kRegCodeMask) << Rd_offset; - } - - static Instr RnSP(Register rn) { - VIXL_ASSERT(!rn.IsZero()); - return (rn.code() & kRegCodeMask) << Rn_offset; - } - - // Flags encoding. - static Instr Flags(FlagsUpdate S) { - if (S == SetFlags) { - return 1 << FlagsUpdate_offset; - } else if (S == LeaveFlags) { - return 0 << FlagsUpdate_offset; - } - VIXL_UNREACHABLE(); - return 0; - } - - static Instr Cond(Condition cond) { - return cond << Condition_offset; - } - - // PC-relative address encoding. - static Instr ImmPCRelAddress(int imm21) { - VIXL_ASSERT(is_int21(imm21)); - Instr imm = static_cast(truncate_to_int21(imm21)); - Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; - Instr immlo = imm << ImmPCRelLo_offset; - return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); - } - - // Branch encoding. - static Instr ImmUncondBranch(int imm26) { - VIXL_ASSERT(is_int26(imm26)); - return truncate_to_int26(imm26) << ImmUncondBranch_offset; - } - - static Instr ImmCondBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCondBranch_offset; - } - - static Instr ImmCmpBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCmpBranch_offset; - } - - static Instr ImmTestBranch(int imm14) { - VIXL_ASSERT(is_int14(imm14)); - return truncate_to_int14(imm14) << ImmTestBranch_offset; - } - - static Instr ImmTestBranchBit(unsigned bit_pos) { - VIXL_ASSERT(is_uint6(bit_pos)); - // Subtract five from the shift offset, as we need bit 5 from bit_pos. - unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); - unsigned b40 = bit_pos << ImmTestBranchBit40_offset; - b5 &= ImmTestBranchBit5_mask; - b40 &= ImmTestBranchBit40_mask; - return b5 | b40; - } - - // Data Processing encoding. - static Instr SF(Register rd) { - return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; - } - - static Instr ImmAddSub(int imm) { - VIXL_ASSERT(IsImmAddSub(imm)); - if (is_uint12(imm)) { // No shift required. - imm <<= ImmAddSub_offset; - } else { - imm = ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); - } - return imm; - } - - static Instr ImmS(unsigned imms, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || - ((reg_size == kWRegSize) && is_uint5(imms))); - USE(reg_size); - return imms << ImmS_offset; - } - - static Instr ImmR(unsigned immr, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - VIXL_ASSERT(is_uint6(immr)); - return immr << ImmR_offset; - } - - static Instr ImmSetBits(unsigned imms, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(is_uint6(imms)); - VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); - USE(reg_size); - return imms << ImmSetBits_offset; - } - - static Instr ImmRotate(unsigned immr, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - return immr << ImmRotate_offset; - } - - static Instr ImmLLiteral(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmLLiteral_offset; - } - - static Instr BitN(unsigned bitn, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); - USE(reg_size); - return bitn << BitN_offset; - } - - static Instr ShiftDP(Shift shift) { - VIXL_ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); - return shift << ShiftDP_offset; - } - - static Instr ImmDPShift(unsigned amount) { - VIXL_ASSERT(is_uint6(amount)); - return amount << ImmDPShift_offset; - } - - static Instr ExtendMode(Extend extend) { - return extend << ExtendMode_offset; - } - - static Instr ImmExtendShift(unsigned left_shift) { - VIXL_ASSERT(left_shift <= 4); - return left_shift << ImmExtendShift_offset; - } - - static Instr ImmCondCmp(unsigned imm) { - VIXL_ASSERT(is_uint5(imm)); - return imm << ImmCondCmp_offset; - } - - static Instr Nzcv(StatusFlags nzcv) { - return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; - } - - // MemOperand offset encoding. - static Instr ImmLSUnsigned(int imm12) { - VIXL_ASSERT(is_uint12(imm12)); - return imm12 << ImmLSUnsigned_offset; - } - - static Instr ImmLS(int imm9) { - VIXL_ASSERT(is_int9(imm9)); - return truncate_to_int9(imm9) << ImmLS_offset; - } - - static Instr ImmLSPair(int imm7, unsigned access_size) { - VIXL_ASSERT(((imm7 >> access_size) << access_size) == imm7); - int scaled_imm7 = imm7 >> access_size; - VIXL_ASSERT(is_int7(scaled_imm7)); - return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; - } - - static Instr ImmShiftLS(unsigned shift_amount) { - VIXL_ASSERT(is_uint1(shift_amount)); - return shift_amount << ImmShiftLS_offset; - } - - static Instr ImmPrefetchOperation(int imm5) { - VIXL_ASSERT(is_uint5(imm5)); - return imm5 << ImmPrefetchOperation_offset; - } - - static Instr ImmException(int imm16) { - VIXL_ASSERT(is_uint16(imm16)); - return imm16 << ImmException_offset; - } - - static Instr ImmSystemRegister(int imm15) { - VIXL_ASSERT(is_uint15(imm15)); - return imm15 << ImmSystemRegister_offset; - } - - static Instr ImmHint(int imm7) { - VIXL_ASSERT(is_uint7(imm7)); - return imm7 << ImmHint_offset; - } - - static Instr CRm(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << CRm_offset; - } - - static Instr CRn(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << CRn_offset; - } - - static Instr SysOp(int imm14) { - VIXL_ASSERT(is_uint14(imm14)); - return imm14 << SysOp_offset; - } - - static Instr ImmSysOp1(int imm3) { - VIXL_ASSERT(is_uint3(imm3)); - return imm3 << SysOp1_offset; - } - - static Instr ImmSysOp2(int imm3) { - VIXL_ASSERT(is_uint3(imm3)); - return imm3 << SysOp2_offset; - } - - static Instr ImmBarrierDomain(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierDomain_offset; - } - - static Instr ImmBarrierType(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierType_offset; - } - - // Move immediates encoding. - static Instr ImmMoveWide(uint64_t imm) { - VIXL_ASSERT(is_uint16(imm)); - return static_cast(imm << ImmMoveWide_offset); - } - - static Instr ShiftMoveWide(int64_t shift) { - VIXL_ASSERT(is_uint2(shift)); - return static_cast(shift << ShiftMoveWide_offset); - } - - // FP Immediates. - static Instr ImmFP32(float imm); - static Instr ImmFP64(double imm); - - // FP register type. - static Instr FPType(FPRegister fd) { - return fd.Is64Bits() ? FP64 : FP32; - } - - static Instr FPScale(unsigned scale) { - VIXL_ASSERT(is_uint6(scale)); - return scale << FPScale_offset; - } - - // Immediate field checking helpers. - static bool IsImmAddSub(int64_t immediate); - static bool IsImmConditionalCompare(int64_t immediate); - static bool IsImmFP32(float imm); - static bool IsImmFP64(double imm); - static bool IsImmLogical(uint64_t value, - unsigned width, - unsigned* n = NULL, - unsigned* imm_s = NULL, - unsigned* imm_r = NULL); - static bool IsImmLSPair(int64_t offset, unsigned access_size); - static bool IsImmLSScaled(int64_t offset, unsigned access_size); - static bool IsImmLSUnscaled(int64_t offset); - static bool IsImmMovn(uint64_t imm, unsigned reg_size); - static bool IsImmMovz(uint64_t imm, unsigned reg_size); - - // Instruction bits for vector format in data processing operations. - static Instr VFormat(VRegister vd) { - if (vd.Is64Bits()) { - switch (vd.lanes()) { - case 2: return NEON_2S; - case 4: return NEON_4H; - case 8: return NEON_8B; - default: return 0xffffffff; - } - } else { - VIXL_ASSERT(vd.Is128Bits()); - switch (vd.lanes()) { - case 2: return NEON_2D; - case 4: return NEON_4S; - case 8: return NEON_8H; - case 16: return NEON_16B; - default: return 0xffffffff; - } - } - } - - // Instruction bits for vector format in floating point data processing - // operations. - static Instr FPFormat(VRegister vd) { - if (vd.lanes() == 1) { - // Floating point scalar formats. - VIXL_ASSERT(vd.Is32Bits() || vd.Is64Bits()); - return vd.Is64Bits() ? FP64 : FP32; - } - - // Two lane floating point vector formats. - if (vd.lanes() == 2) { - VIXL_ASSERT(vd.Is64Bits() || vd.Is128Bits()); - return vd.Is128Bits() ? NEON_FP_2D : NEON_FP_2S; - } - - // Four lane floating point vector format. - VIXL_ASSERT((vd.lanes() == 4) && vd.Is128Bits()); - return NEON_FP_4S; - } - - // Instruction bits for vector format in load and store operations. - static Instr LSVFormat(VRegister vd) { - if (vd.Is64Bits()) { - switch (vd.lanes()) { - case 1: return LS_NEON_1D; - case 2: return LS_NEON_2S; - case 4: return LS_NEON_4H; - case 8: return LS_NEON_8B; - default: return 0xffffffff; - } - } else { - VIXL_ASSERT(vd.Is128Bits()); - switch (vd.lanes()) { - case 2: return LS_NEON_2D; - case 4: return LS_NEON_4S; - case 8: return LS_NEON_8H; - case 16: return LS_NEON_16B; - default: return 0xffffffff; - } - } - } - - // Instruction bits for scalar format in data processing operations. - static Instr SFormat(VRegister vd) { - VIXL_ASSERT(vd.lanes() == 1); - switch (vd.SizeInBytes()) { - case 1: return NEON_B; - case 2: return NEON_H; - case 4: return NEON_S; - case 8: return NEON_D; - default: return 0xffffffff; - } - } - - static Instr ImmNEONHLM(int index, int num_bits) { - int h, l, m; - if (num_bits == 3) { - VIXL_ASSERT(is_uint3(index)); - h = (index >> 2) & 1; - l = (index >> 1) & 1; - m = (index >> 0) & 1; - } else if (num_bits == 2) { - VIXL_ASSERT(is_uint2(index)); - h = (index >> 1) & 1; - l = (index >> 0) & 1; - m = 0; - } else { - VIXL_ASSERT(is_uint1(index) && (num_bits == 1)); - h = (index >> 0) & 1; - l = 0; - m = 0; - } - return (h << NEONH_offset) | (l << NEONL_offset) | (m << NEONM_offset); - } - - static Instr ImmNEONExt(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << ImmNEONExt_offset; - } - - static Instr ImmNEON5(Instr format, int index) { - VIXL_ASSERT(is_uint4(index)); - int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); - int imm5 = (index << (s + 1)) | (1 << s); - return imm5 << ImmNEON5_offset; - } - - static Instr ImmNEON4(Instr format, int index) { - VIXL_ASSERT(is_uint4(index)); - int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); - int imm4 = index << s; - return imm4 << ImmNEON4_offset; - } - - static Instr ImmNEONabcdefgh(int imm8) { - VIXL_ASSERT(is_uint8(imm8)); - Instr instr; - instr = ((imm8 >> 5) & 7) << ImmNEONabc_offset; - instr |= (imm8 & 0x1f) << ImmNEONdefgh_offset; - return instr; - } - - static Instr NEONCmode(int cmode) { - VIXL_ASSERT(is_uint4(cmode)); - return cmode << NEONCmode_offset; - } - - static Instr NEONModImmOp(int op) { - VIXL_ASSERT(is_uint1(op)); - return op << NEONModImmOp_offset; - } - - // Size of the code generated since label to the current position. - size_t SizeOfCodeGeneratedSince(Label* label) const { - VIXL_ASSERT(label->IsBound()); - return buffer_->OffsetFrom(label->location()); - } - - size_t SizeOfCodeGenerated() const { - return buffer_->CursorOffset(); - } - - size_t BufferCapacity() const { return buffer_->capacity(); } - - size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); } - - void EnsureSpaceFor(size_t amount) { - if (buffer_->RemainingBytes() < amount) { - size_t capacity = buffer_->capacity(); - size_t size = buffer_->CursorOffset(); - do { - // TODO(all): refine. - capacity *= 2; - } while ((capacity - size) < amount); - buffer_->Grow(capacity); - } - } - -#ifdef VIXL_DEBUG - void AcquireBuffer() { - VIXL_ASSERT(buffer_monitor_ >= 0); - buffer_monitor_++; - } - - void ReleaseBuffer() { - buffer_monitor_--; - VIXL_ASSERT(buffer_monitor_ >= 0); - } -#endif - - PositionIndependentCodeOption pic() const { - return pic_; - } - - bool AllowPageOffsetDependentCode() const { - return (pic() == PageOffsetDependentCode) || - (pic() == PositionDependentCode); - } - - static const Register& AppropriateZeroRegFor(const CPURegister& reg) { - return reg.Is64Bits() ? xzr : wzr; - } - - - protected: - void LoadStore(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op, - LoadStoreScalingOption option = PreferScaledOffset); - - void LoadStorePair(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairOp op); - void LoadStoreStruct(const VRegister& vt, - const MemOperand& addr, - NEONLoadStoreMultiStructOp op); - void LoadStoreStruct1(const VRegister& vt, - int reg_count, - const MemOperand& addr); - void LoadStoreStructSingle(const VRegister& vt, - uint32_t lane, - const MemOperand& addr, - NEONLoadStoreSingleStructOp op); - void LoadStoreStructSingleAllLanes(const VRegister& vt, - const MemOperand& addr, - NEONLoadStoreSingleStructOp op); - void LoadStoreStructVerify(const VRegister& vt, - const MemOperand& addr, - Instr op); - - void Prefetch(PrefetchOperation op, - const MemOperand& addr, - LoadStoreScalingOption option = PreferScaledOffset); - - // TODO(all): The third parameter should be passed by reference but gcc 4.8.2 - // reports a bogus uninitialised warning then. - void Logical(const Register& rd, - const Register& rn, - const Operand operand, - LogicalOp op); - void LogicalImmediate(const Register& rd, - const Register& rn, - unsigned n, - unsigned imm_s, - unsigned imm_r, - LogicalOp op); - - void ConditionalCompare(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op); - - void AddSubWithCarry(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op); - - - // Functions for emulating operands not directly supported by the instruction - // set. - void EmitShift(const Register& rd, - const Register& rn, - Shift shift, - unsigned amount); - void EmitExtendShift(const Register& rd, - const Register& rn, - Extend extend, - unsigned left_shift); - - void AddSub(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op); - - void NEONTable(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEONTableOp op); - - // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified - // registers. Only simple loads are supported; sign- and zero-extension (such - // as in LDPSW_x or LDRB_w) are not supported. - static LoadStoreOp LoadOpFor(const CPURegister& rt); - static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static LoadStoreOp StoreOpFor(const CPURegister& rt); - static LoadStorePairOp StorePairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static LoadLiteralOp LoadLiteralOpFor(const CPURegister& rt); - - - private: - static uint32_t FP32ToImm8(float imm); - static uint32_t FP64ToImm8(double imm); - - // Instruction helpers. - void MoveWide(const Register& rd, - uint64_t imm, - int shift, - MoveWideImmediateOp mov_op); - void DataProcShiftedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void DataProcExtendedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void LoadStorePairNonTemporal(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairNonTemporalOp op); - void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); - void ConditionalSelect(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond, - ConditionalSelectOp op); - void DataProcessing1Source(const Register& rd, - const Register& rn, - DataProcessing1SourceOp op); - void DataProcessing3Source(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra, - DataProcessing3SourceOp op); - void FPDataProcessing1Source(const VRegister& fd, - const VRegister& fn, - FPDataProcessing1SourceOp op); - void FPDataProcessing3Source(const VRegister& fd, - const VRegister& fn, - const VRegister& fm, - const VRegister& fa, - FPDataProcessing3SourceOp op); - void NEONAcrossLanesL(const VRegister& vd, - const VRegister& vn, - NEONAcrossLanesOp op); - void NEONAcrossLanes(const VRegister& vd, - const VRegister& vn, - NEONAcrossLanesOp op); - void NEONModifiedImmShiftLsl(const VRegister& vd, - const int imm8, - const int left_shift, - NEONModifiedImmediateOp op); - void NEONModifiedImmShiftMsl(const VRegister& vd, - const int imm8, - const int shift_amount, - NEONModifiedImmediateOp op); - void NEONFP2Same(const VRegister& vd, - const VRegister& vn, - Instr vop); - void NEON3Same(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3SameOp vop); - void NEONFP3Same(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - Instr op); - void NEON3DifferentL(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3DifferentOp vop); - void NEON3DifferentW(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3DifferentOp vop); - void NEON3DifferentHN(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3DifferentOp vop); - void NEONFP2RegMisc(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp vop, - double value = 0.0); - void NEON2RegMisc(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp vop, - int value = 0); - void NEONFP2RegMisc(const VRegister& vd, - const VRegister& vn, - Instr op); - void NEONAddlp(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp op); - void NEONPerm(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEONPermOp op); - void NEONFPByElement(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index, - NEONByIndexedElementOp op); - void NEONByElement(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index, - NEONByIndexedElementOp op); - void NEONByElementL(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index, - NEONByIndexedElementOp op); - void NEONShiftImmediate(const VRegister& vd, - const VRegister& vn, - NEONShiftImmediateOp op, - int immh_immb); - void NEONShiftLeftImmediate(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONShiftRightImmediate(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONShiftImmediateL(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONShiftImmediateN(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONXtn(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp vop); - - Instr LoadStoreStructAddrModeField(const MemOperand& addr); - - // Encode the specified MemOperand for the specified access size and scaling - // preference. - Instr LoadStoreMemOperand(const MemOperand& addr, - unsigned access_size, - LoadStoreScalingOption option); - - // Link the current (not-yet-emitted) instruction to the specified label, then - // return an offset to be encoded in the instruction. If the label is not yet - // bound, an offset of 0 is returned. - ptrdiff_t LinkAndGetByteOffsetTo(Label * label); - ptrdiff_t LinkAndGetInstructionOffsetTo(Label * label); - ptrdiff_t LinkAndGetPageOffsetTo(Label * label); - - // A common implementation for the LinkAndGetOffsetTo helpers. - template - ptrdiff_t LinkAndGetOffsetTo(Label* label); - - // Literal load offset are in words (32-bit). - ptrdiff_t LinkAndGetWordOffsetTo(RawLiteral* literal); - - // Emit the instruction in buffer_. - void Emit(Instr instruction) { - VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize); - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit32(instruction); - } - - // Buffer where the code is emitted. - CodeBuffer* buffer_; - PositionIndependentCodeOption pic_; - -#ifdef VIXL_DEBUG - int64_t buffer_monitor_; -#endif -}; - - -// All Assembler emits MUST acquire/release the underlying code buffer. The -// helper scope below will do so and optionally ensure the buffer is big enough -// to receive the emit. It is possible to request the scope not to perform any -// checks (kNoCheck) if for example it is known in advance the buffer size is -// adequate or there is some other size checking mechanism in place. -class CodeBufferCheckScope { - public: - // Tell whether or not the scope needs to ensure the associated CodeBuffer - // has enough space for the requested size. - enum CheckPolicy { - kNoCheck, - kCheck - }; - - // Tell whether or not the scope should assert the amount of code emitted - // within the scope is consistent with the requested amount. - enum AssertPolicy { - kNoAssert, // No assert required. - kExactSize, // The code emitted must be exactly size bytes. - kMaximumSize // The code emitted must be at most size bytes. - }; - - CodeBufferCheckScope(Assembler* assm, - size_t size, - CheckPolicy check_policy = kCheck, - AssertPolicy assert_policy = kMaximumSize) - : assm_(assm) { - if (check_policy == kCheck) assm->EnsureSpaceFor(size); -#ifdef VIXL_DEBUG - assm->bind(&start_); - size_ = size; - assert_policy_ = assert_policy; - assm->AcquireBuffer(); -#else - USE(assert_policy); -#endif - } - - // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert). - explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) { -#ifdef VIXL_DEBUG - size_ = 0; - assert_policy_ = kNoAssert; - assm->AcquireBuffer(); -#endif - } - - ~CodeBufferCheckScope() { -#ifdef VIXL_DEBUG - assm_->ReleaseBuffer(); - switch (assert_policy_) { - case kNoAssert: break; - case kExactSize: - VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) == size_); - break; - case kMaximumSize: - VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) <= size_); - break; - default: - VIXL_UNREACHABLE(); - } -#endif - } - - protected: - Assembler* assm_; -#ifdef VIXL_DEBUG - Label start_; - size_t size_; - AssertPolicy assert_policy_; -#endif -}; - - -template -void Literal::UpdateValue(T new_value, const Assembler* assembler) { - return UpdateValue(new_value, assembler->GetStartAddress()); -} - - -template -void Literal::UpdateValue(T high64, T low64, const Assembler* assembler) { - return UpdateValue(high64, low64, assembler->GetStartAddress()); -} - - -} // namespace vixl - -#endif // VIXL_A64_ASSEMBLER_A64_H_ diff --git a/disas/libvixl/vixl/a64/constants-a64.h b/disas/libvixl/vixl/a64/constants-a64.h deleted file mode 100644 index 2caa73af87f..00000000000 --- a/disas/libvixl/vixl/a64/constants-a64.h +++ /dev/null @@ -1,2116 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_CONSTANTS_A64_H_ -#define VIXL_A64_CONSTANTS_A64_H_ - -namespace vixl { - -const unsigned kNumberOfRegisters = 32; -const unsigned kNumberOfVRegisters = 32; -const unsigned kNumberOfFPRegisters = kNumberOfVRegisters; -// Callee saved registers are x21-x30(lr). -const int kNumberOfCalleeSavedRegisters = 10; -const int kFirstCalleeSavedRegisterIndex = 21; -// Callee saved FP registers are d8-d15. -const int kNumberOfCalleeSavedFPRegisters = 8; -const int kFirstCalleeSavedFPRegisterIndex = 8; - -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - -#define INSTRUCTION_FIELDS_LIST(V_) \ -/* Register fields */ \ -V_(Rd, 4, 0, Bits) /* Destination register. */ \ -V_(Rn, 9, 5, Bits) /* First source register. */ \ -V_(Rm, 20, 16, Bits) /* Second source register. */ \ -V_(Ra, 14, 10, Bits) /* Third source register. */ \ -V_(Rt, 4, 0, Bits) /* Load/store register. */ \ -V_(Rt2, 14, 10, Bits) /* Load/store second register. */ \ -V_(Rs, 20, 16, Bits) /* Exclusive access status. */ \ - \ -/* Common bits */ \ -V_(SixtyFourBits, 31, 31, Bits) \ -V_(FlagsUpdate, 29, 29, Bits) \ - \ -/* PC relative addressing */ \ -V_(ImmPCRelHi, 23, 5, SignedBits) \ -V_(ImmPCRelLo, 30, 29, Bits) \ - \ -/* Add/subtract/logical shift register */ \ -V_(ShiftDP, 23, 22, Bits) \ -V_(ImmDPShift, 15, 10, Bits) \ - \ -/* Add/subtract immediate */ \ -V_(ImmAddSub, 21, 10, Bits) \ -V_(ShiftAddSub, 23, 22, Bits) \ - \ -/* Add/substract extend */ \ -V_(ImmExtendShift, 12, 10, Bits) \ -V_(ExtendMode, 15, 13, Bits) \ - \ -/* Move wide */ \ -V_(ImmMoveWide, 20, 5, Bits) \ -V_(ShiftMoveWide, 22, 21, Bits) \ - \ -/* Logical immediate, bitfield and extract */ \ -V_(BitN, 22, 22, Bits) \ -V_(ImmRotate, 21, 16, Bits) \ -V_(ImmSetBits, 15, 10, Bits) \ -V_(ImmR, 21, 16, Bits) \ -V_(ImmS, 15, 10, Bits) \ - \ -/* Test and branch immediate */ \ -V_(ImmTestBranch, 18, 5, SignedBits) \ -V_(ImmTestBranchBit40, 23, 19, Bits) \ -V_(ImmTestBranchBit5, 31, 31, Bits) \ - \ -/* Conditionals */ \ -V_(Condition, 15, 12, Bits) \ -V_(ConditionBranch, 3, 0, Bits) \ -V_(Nzcv, 3, 0, Bits) \ -V_(ImmCondCmp, 20, 16, Bits) \ -V_(ImmCondBranch, 23, 5, SignedBits) \ - \ -/* Floating point */ \ -V_(FPType, 23, 22, Bits) \ -V_(ImmFP, 20, 13, Bits) \ -V_(FPScale, 15, 10, Bits) \ - \ -/* Load Store */ \ -V_(ImmLS, 20, 12, SignedBits) \ -V_(ImmLSUnsigned, 21, 10, Bits) \ -V_(ImmLSPair, 21, 15, SignedBits) \ -V_(ImmShiftLS, 12, 12, Bits) \ -V_(LSOpc, 23, 22, Bits) \ -V_(LSVector, 26, 26, Bits) \ -V_(LSSize, 31, 30, Bits) \ -V_(ImmPrefetchOperation, 4, 0, Bits) \ -V_(PrefetchHint, 4, 3, Bits) \ -V_(PrefetchTarget, 2, 1, Bits) \ -V_(PrefetchStream, 0, 0, Bits) \ - \ -/* Other immediates */ \ -V_(ImmUncondBranch, 25, 0, SignedBits) \ -V_(ImmCmpBranch, 23, 5, SignedBits) \ -V_(ImmLLiteral, 23, 5, SignedBits) \ -V_(ImmException, 20, 5, Bits) \ -V_(ImmHint, 11, 5, Bits) \ -V_(ImmBarrierDomain, 11, 10, Bits) \ -V_(ImmBarrierType, 9, 8, Bits) \ - \ -/* System (MRS, MSR, SYS) */ \ -V_(ImmSystemRegister, 19, 5, Bits) \ -V_(SysO0, 19, 19, Bits) \ -V_(SysOp, 18, 5, Bits) \ -V_(SysOp1, 18, 16, Bits) \ -V_(SysOp2, 7, 5, Bits) \ -V_(CRn, 15, 12, Bits) \ -V_(CRm, 11, 8, Bits) \ - \ -/* Load-/store-exclusive */ \ -V_(LdStXLoad, 22, 22, Bits) \ -V_(LdStXNotExclusive, 23, 23, Bits) \ -V_(LdStXAcquireRelease, 15, 15, Bits) \ -V_(LdStXSizeLog2, 31, 30, Bits) \ -V_(LdStXPair, 21, 21, Bits) \ - \ -/* NEON generic fields */ \ -V_(NEONQ, 30, 30, Bits) \ -V_(NEONSize, 23, 22, Bits) \ -V_(NEONLSSize, 11, 10, Bits) \ -V_(NEONS, 12, 12, Bits) \ -V_(NEONL, 21, 21, Bits) \ -V_(NEONM, 20, 20, Bits) \ -V_(NEONH, 11, 11, Bits) \ -V_(ImmNEONExt, 14, 11, Bits) \ -V_(ImmNEON5, 20, 16, Bits) \ -V_(ImmNEON4, 14, 11, Bits) \ - \ -/* NEON Modified Immediate fields */ \ -V_(ImmNEONabc, 18, 16, Bits) \ -V_(ImmNEONdefgh, 9, 5, Bits) \ -V_(NEONModImmOp, 29, 29, Bits) \ -V_(NEONCmode, 15, 12, Bits) \ - \ -/* NEON Shift Immediate fields */ \ -V_(ImmNEONImmhImmb, 22, 16, Bits) \ -V_(ImmNEONImmh, 22, 19, Bits) \ -V_(ImmNEONImmb, 18, 16, Bits) - -#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ -/* NZCV */ \ -V_(Flags, 31, 28, Bits) \ -V_(N, 31, 31, Bits) \ -V_(Z, 30, 30, Bits) \ -V_(C, 29, 29, Bits) \ -V_(V, 28, 28, Bits) \ -M_(NZCV, Flags_mask) \ -/* FPCR */ \ -V_(AHP, 26, 26, Bits) \ -V_(DN, 25, 25, Bits) \ -V_(FZ, 24, 24, Bits) \ -V_(RMode, 23, 22, Bits) \ -M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) - -// Fields offsets. -#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ -const int Name##_offset = LowBit; \ -const int Name##_width = HighBit - LowBit + 1; \ -const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; -#define NOTHING(A, B) -INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) -SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) -#undef NOTHING -#undef DECLARE_FIELDS_BITS - -// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed -// from ImmPCRelLo and ImmPCRelHi. -const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; - -// Condition codes. -enum Condition { - eq = 0, // Z set Equal. - ne = 1, // Z clear Not equal. - cs = 2, // C set Carry set. - cc = 3, // C clear Carry clear. - mi = 4, // N set Negative. - pl = 5, // N clear Positive or zero. - vs = 6, // V set Overflow. - vc = 7, // V clear No overflow. - hi = 8, // C set, Z clear Unsigned higher. - ls = 9, // C clear or Z set Unsigned lower or same. - ge = 10, // N == V Greater or equal. - lt = 11, // N != V Less than. - gt = 12, // Z clear, N == V Greater than. - le = 13, // Z set or N != V Less then or equal - al = 14, // Always. - nv = 15, // Behaves as always/al. - - // Aliases. - hs = cs, // C set Unsigned higher or same. - lo = cc // C clear Unsigned lower. -}; - -inline Condition InvertCondition(Condition cond) { - // Conditions al and nv behave identically, as "always true". They can't be - // inverted, because there is no "always false" condition. - VIXL_ASSERT((cond != al) && (cond != nv)); - return static_cast(cond ^ 1); -} - -enum FPTrapFlags { - EnableTrap = 1, - DisableTrap = 0 -}; - -enum FlagsUpdate { - SetFlags = 1, - LeaveFlags = 0 -}; - -enum StatusFlags { - NoFlag = 0, - - // Derive the flag combinations from the system register bit descriptions. - NFlag = N_mask, - ZFlag = Z_mask, - CFlag = C_mask, - VFlag = V_mask, - NZFlag = NFlag | ZFlag, - NCFlag = NFlag | CFlag, - NVFlag = NFlag | VFlag, - ZCFlag = ZFlag | CFlag, - ZVFlag = ZFlag | VFlag, - CVFlag = CFlag | VFlag, - NZCFlag = NFlag | ZFlag | CFlag, - NZVFlag = NFlag | ZFlag | VFlag, - NCVFlag = NFlag | CFlag | VFlag, - ZCVFlag = ZFlag | CFlag | VFlag, - NZCVFlag = NFlag | ZFlag | CFlag | VFlag, - - // Floating-point comparison results. - FPEqualFlag = ZCFlag, - FPLessThanFlag = NFlag, - FPGreaterThanFlag = CFlag, - FPUnorderedFlag = CVFlag -}; - -enum Shift { - NO_SHIFT = -1, - LSL = 0x0, - LSR = 0x1, - ASR = 0x2, - ROR = 0x3, - MSL = 0x4 -}; - -enum Extend { - NO_EXTEND = -1, - UXTB = 0, - UXTH = 1, - UXTW = 2, - UXTX = 3, - SXTB = 4, - SXTH = 5, - SXTW = 6, - SXTX = 7 -}; - -enum SystemHint { - NOP = 0, - YIELD = 1, - WFE = 2, - WFI = 3, - SEV = 4, - SEVL = 5 -}; - -enum BarrierDomain { - OuterShareable = 0, - NonShareable = 1, - InnerShareable = 2, - FullSystem = 3 -}; - -enum BarrierType { - BarrierOther = 0, - BarrierReads = 1, - BarrierWrites = 2, - BarrierAll = 3 -}; - -enum PrefetchOperation { - PLDL1KEEP = 0x00, - PLDL1STRM = 0x01, - PLDL2KEEP = 0x02, - PLDL2STRM = 0x03, - PLDL3KEEP = 0x04, - PLDL3STRM = 0x05, - - PLIL1KEEP = 0x08, - PLIL1STRM = 0x09, - PLIL2KEEP = 0x0a, - PLIL2STRM = 0x0b, - PLIL3KEEP = 0x0c, - PLIL3STRM = 0x0d, - - PSTL1KEEP = 0x10, - PSTL1STRM = 0x11, - PSTL2KEEP = 0x12, - PSTL2STRM = 0x13, - PSTL3KEEP = 0x14, - PSTL3STRM = 0x15 -}; - -// System/special register names. -// This information is not encoded as one field but as the concatenation of -// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). -enum SystemRegister { - NZCV = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x2 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, - FPCR = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x4 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset -}; - -enum InstructionCacheOp { - IVAU = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0x5 << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset -}; - -enum DataCacheOp { - CVAC = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0xa << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset, - CVAU = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0xb << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset, - CIVAC = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0xe << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset, - ZVA = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0x4 << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset -}; - -// Instruction enumerations. -// -// These are the masks that define a class of instructions, and the list of -// instructions within each class. Each enumeration has a Fixed, FMask and -// Mask value. -// -// Fixed: The fixed bits in this instruction class. -// FMask: The mask used to extract the fixed bits in the class. -// Mask: The mask used to identify the instructions within a class. -// -// The enumerations can be used like this: -// -// VIXL_ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); -// switch(instr->Mask(PCRelAddressingMask)) { -// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; -// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; -// default: printf("Unknown instruction\n"); -// } - - -// Generic fields. -enum GenericInstrField { - SixtyFourBits = 0x80000000, - ThirtyTwoBits = 0x00000000, - FP32 = 0x00000000, - FP64 = 0x00400000 -}; - -enum NEONFormatField { - NEONFormatFieldMask = 0x40C00000, - NEON_Q = 0x40000000, - NEON_8B = 0x00000000, - NEON_16B = NEON_8B | NEON_Q, - NEON_4H = 0x00400000, - NEON_8H = NEON_4H | NEON_Q, - NEON_2S = 0x00800000, - NEON_4S = NEON_2S | NEON_Q, - NEON_1D = 0x00C00000, - NEON_2D = 0x00C00000 | NEON_Q -}; - -enum NEONFPFormatField { - NEONFPFormatFieldMask = 0x40400000, - NEON_FP_2S = FP32, - NEON_FP_4S = FP32 | NEON_Q, - NEON_FP_2D = FP64 | NEON_Q -}; - -enum NEONLSFormatField { - NEONLSFormatFieldMask = 0x40000C00, - LS_NEON_8B = 0x00000000, - LS_NEON_16B = LS_NEON_8B | NEON_Q, - LS_NEON_4H = 0x00000400, - LS_NEON_8H = LS_NEON_4H | NEON_Q, - LS_NEON_2S = 0x00000800, - LS_NEON_4S = LS_NEON_2S | NEON_Q, - LS_NEON_1D = 0x00000C00, - LS_NEON_2D = LS_NEON_1D | NEON_Q -}; - -enum NEONScalarFormatField { - NEONScalarFormatFieldMask = 0x00C00000, - NEONScalar = 0x10000000, - NEON_B = 0x00000000, - NEON_H = 0x00400000, - NEON_S = 0x00800000, - NEON_D = 0x00C00000 -}; - -// PC relative addressing. -enum PCRelAddressingOp { - PCRelAddressingFixed = 0x10000000, - PCRelAddressingFMask = 0x1F000000, - PCRelAddressingMask = 0x9F000000, - ADR = PCRelAddressingFixed | 0x00000000, - ADRP = PCRelAddressingFixed | 0x80000000 -}; - -// Add/sub (immediate, shifted and extended.) -const int kSFOffset = 31; -enum AddSubOp { - AddSubOpMask = 0x60000000, - AddSubSetFlagsBit = 0x20000000, - ADD = 0x00000000, - ADDS = ADD | AddSubSetFlagsBit, - SUB = 0x40000000, - SUBS = SUB | AddSubSetFlagsBit -}; - -#define ADD_SUB_OP_LIST(V) \ - V(ADD), \ - V(ADDS), \ - V(SUB), \ - V(SUBS) - -enum AddSubImmediateOp { - AddSubImmediateFixed = 0x11000000, - AddSubImmediateFMask = 0x1F000000, - AddSubImmediateMask = 0xFF000000, - #define ADD_SUB_IMMEDIATE(A) \ - A##_w_imm = AddSubImmediateFixed | A, \ - A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) - #undef ADD_SUB_IMMEDIATE -}; - -enum AddSubShiftedOp { - AddSubShiftedFixed = 0x0B000000, - AddSubShiftedFMask = 0x1F200000, - AddSubShiftedMask = 0xFF200000, - #define ADD_SUB_SHIFTED(A) \ - A##_w_shift = AddSubShiftedFixed | A, \ - A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) - #undef ADD_SUB_SHIFTED -}; - -enum AddSubExtendedOp { - AddSubExtendedFixed = 0x0B200000, - AddSubExtendedFMask = 0x1F200000, - AddSubExtendedMask = 0xFFE00000, - #define ADD_SUB_EXTENDED(A) \ - A##_w_ext = AddSubExtendedFixed | A, \ - A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) - #undef ADD_SUB_EXTENDED -}; - -// Add/sub with carry. -enum AddSubWithCarryOp { - AddSubWithCarryFixed = 0x1A000000, - AddSubWithCarryFMask = 0x1FE00000, - AddSubWithCarryMask = 0xFFE0FC00, - ADC_w = AddSubWithCarryFixed | ADD, - ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, - ADC = ADC_w, - ADCS_w = AddSubWithCarryFixed | ADDS, - ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, - SBC_w = AddSubWithCarryFixed | SUB, - SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, - SBC = SBC_w, - SBCS_w = AddSubWithCarryFixed | SUBS, - SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits -}; - - -// Logical (immediate and shifted register). -enum LogicalOp { - LogicalOpMask = 0x60200000, - NOT = 0x00200000, - AND = 0x00000000, - BIC = AND | NOT, - ORR = 0x20000000, - ORN = ORR | NOT, - EOR = 0x40000000, - EON = EOR | NOT, - ANDS = 0x60000000, - BICS = ANDS | NOT -}; - -// Logical immediate. -enum LogicalImmediateOp { - LogicalImmediateFixed = 0x12000000, - LogicalImmediateFMask = 0x1F800000, - LogicalImmediateMask = 0xFF800000, - AND_w_imm = LogicalImmediateFixed | AND, - AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, - ORR_w_imm = LogicalImmediateFixed | ORR, - ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, - EOR_w_imm = LogicalImmediateFixed | EOR, - EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, - ANDS_w_imm = LogicalImmediateFixed | ANDS, - ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits -}; - -// Logical shifted register. -enum LogicalShiftedOp { - LogicalShiftedFixed = 0x0A000000, - LogicalShiftedFMask = 0x1F000000, - LogicalShiftedMask = 0xFF200000, - AND_w = LogicalShiftedFixed | AND, - AND_x = LogicalShiftedFixed | AND | SixtyFourBits, - AND_shift = AND_w, - BIC_w = LogicalShiftedFixed | BIC, - BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, - BIC_shift = BIC_w, - ORR_w = LogicalShiftedFixed | ORR, - ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, - ORR_shift = ORR_w, - ORN_w = LogicalShiftedFixed | ORN, - ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, - ORN_shift = ORN_w, - EOR_w = LogicalShiftedFixed | EOR, - EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, - EOR_shift = EOR_w, - EON_w = LogicalShiftedFixed | EON, - EON_x = LogicalShiftedFixed | EON | SixtyFourBits, - EON_shift = EON_w, - ANDS_w = LogicalShiftedFixed | ANDS, - ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, - ANDS_shift = ANDS_w, - BICS_w = LogicalShiftedFixed | BICS, - BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, - BICS_shift = BICS_w -}; - -// Move wide immediate. -enum MoveWideImmediateOp { - MoveWideImmediateFixed = 0x12800000, - MoveWideImmediateFMask = 0x1F800000, - MoveWideImmediateMask = 0xFF800000, - MOVN = 0x00000000, - MOVZ = 0x40000000, - MOVK = 0x60000000, - MOVN_w = MoveWideImmediateFixed | MOVN, - MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, - MOVZ_w = MoveWideImmediateFixed | MOVZ, - MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, - MOVK_w = MoveWideImmediateFixed | MOVK, - MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits -}; - -// Bitfield. -const int kBitfieldNOffset = 22; -enum BitfieldOp { - BitfieldFixed = 0x13000000, - BitfieldFMask = 0x1F800000, - BitfieldMask = 0xFF800000, - SBFM_w = BitfieldFixed | 0x00000000, - SBFM_x = BitfieldFixed | 0x80000000, - SBFM = SBFM_w, - BFM_w = BitfieldFixed | 0x20000000, - BFM_x = BitfieldFixed | 0xA0000000, - BFM = BFM_w, - UBFM_w = BitfieldFixed | 0x40000000, - UBFM_x = BitfieldFixed | 0xC0000000, - UBFM = UBFM_w - // Bitfield N field. -}; - -// Extract. -enum ExtractOp { - ExtractFixed = 0x13800000, - ExtractFMask = 0x1F800000, - ExtractMask = 0xFFA00000, - EXTR_w = ExtractFixed | 0x00000000, - EXTR_x = ExtractFixed | 0x80000000, - EXTR = EXTR_w -}; - -// Unconditional branch. -enum UnconditionalBranchOp { - UnconditionalBranchFixed = 0x14000000, - UnconditionalBranchFMask = 0x7C000000, - UnconditionalBranchMask = 0xFC000000, - B = UnconditionalBranchFixed | 0x00000000, - BL = UnconditionalBranchFixed | 0x80000000 -}; - -// Unconditional branch to register. -enum UnconditionalBranchToRegisterOp { - UnconditionalBranchToRegisterFixed = 0xD6000000, - UnconditionalBranchToRegisterFMask = 0xFE000000, - UnconditionalBranchToRegisterMask = 0xFFFFFC1F, - BR = UnconditionalBranchToRegisterFixed | 0x001F0000, - BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, - RET = UnconditionalBranchToRegisterFixed | 0x005F0000 -}; - -// Compare and branch. -enum CompareBranchOp { - CompareBranchFixed = 0x34000000, - CompareBranchFMask = 0x7E000000, - CompareBranchMask = 0xFF000000, - CBZ_w = CompareBranchFixed | 0x00000000, - CBZ_x = CompareBranchFixed | 0x80000000, - CBZ = CBZ_w, - CBNZ_w = CompareBranchFixed | 0x01000000, - CBNZ_x = CompareBranchFixed | 0x81000000, - CBNZ = CBNZ_w -}; - -// Test and branch. -enum TestBranchOp { - TestBranchFixed = 0x36000000, - TestBranchFMask = 0x7E000000, - TestBranchMask = 0x7F000000, - TBZ = TestBranchFixed | 0x00000000, - TBNZ = TestBranchFixed | 0x01000000 -}; - -// Conditional branch. -enum ConditionalBranchOp { - ConditionalBranchFixed = 0x54000000, - ConditionalBranchFMask = 0xFE000000, - ConditionalBranchMask = 0xFF000010, - B_cond = ConditionalBranchFixed | 0x00000000 -}; - -// System. -// System instruction encoding is complicated because some instructions use op -// and CR fields to encode parameters. To handle this cleanly, the system -// instructions are split into more than one enum. - -enum SystemOp { - SystemFixed = 0xD5000000, - SystemFMask = 0xFFC00000 -}; - -enum SystemSysRegOp { - SystemSysRegFixed = 0xD5100000, - SystemSysRegFMask = 0xFFD00000, - SystemSysRegMask = 0xFFF00000, - MRS = SystemSysRegFixed | 0x00200000, - MSR = SystemSysRegFixed | 0x00000000 -}; - -enum SystemHintOp { - SystemHintFixed = 0xD503201F, - SystemHintFMask = 0xFFFFF01F, - SystemHintMask = 0xFFFFF01F, - HINT = SystemHintFixed | 0x00000000 -}; - -enum SystemSysOp { - SystemSysFixed = 0xD5080000, - SystemSysFMask = 0xFFF80000, - SystemSysMask = 0xFFF80000, - SYS = SystemSysFixed | 0x00000000 -}; - -// Exception. -enum ExceptionOp { - ExceptionFixed = 0xD4000000, - ExceptionFMask = 0xFF000000, - ExceptionMask = 0xFFE0001F, - HLT = ExceptionFixed | 0x00400000, - BRK = ExceptionFixed | 0x00200000, - SVC = ExceptionFixed | 0x00000001, - HVC = ExceptionFixed | 0x00000002, - SMC = ExceptionFixed | 0x00000003, - DCPS1 = ExceptionFixed | 0x00A00001, - DCPS2 = ExceptionFixed | 0x00A00002, - DCPS3 = ExceptionFixed | 0x00A00003 -}; - -enum MemBarrierOp { - MemBarrierFixed = 0xD503309F, - MemBarrierFMask = 0xFFFFF09F, - MemBarrierMask = 0xFFFFF0FF, - DSB = MemBarrierFixed | 0x00000000, - DMB = MemBarrierFixed | 0x00000020, - ISB = MemBarrierFixed | 0x00000040 -}; - -enum SystemExclusiveMonitorOp { - SystemExclusiveMonitorFixed = 0xD503305F, - SystemExclusiveMonitorFMask = 0xFFFFF0FF, - SystemExclusiveMonitorMask = 0xFFFFF0FF, - CLREX = SystemExclusiveMonitorFixed -}; - -// Any load or store. -enum LoadStoreAnyOp { - LoadStoreAnyFMask = 0x0a000000, - LoadStoreAnyFixed = 0x08000000 -}; - -// Any load pair or store pair. -enum LoadStorePairAnyOp { - LoadStorePairAnyFMask = 0x3a000000, - LoadStorePairAnyFixed = 0x28000000 -}; - -#define LOAD_STORE_PAIR_OP_LIST(V) \ - V(STP, w, 0x00000000), \ - V(LDP, w, 0x00400000), \ - V(LDPSW, x, 0x40400000), \ - V(STP, x, 0x80000000), \ - V(LDP, x, 0x80400000), \ - V(STP, s, 0x04000000), \ - V(LDP, s, 0x04400000), \ - V(STP, d, 0x44000000), \ - V(LDP, d, 0x44400000), \ - V(STP, q, 0x84000000), \ - V(LDP, q, 0x84400000) - -// Load/store pair (post, pre and offset.) -enum LoadStorePairOp { - LoadStorePairMask = 0xC4400000, - LoadStorePairLBit = 1 << 22, - #define LOAD_STORE_PAIR(A, B, C) \ - A##_##B = C - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) - #undef LOAD_STORE_PAIR -}; - -enum LoadStorePairPostIndexOp { - LoadStorePairPostIndexFixed = 0x28800000, - LoadStorePairPostIndexFMask = 0x3B800000, - LoadStorePairPostIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ - A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) - #undef LOAD_STORE_PAIR_POST_INDEX -}; - -enum LoadStorePairPreIndexOp { - LoadStorePairPreIndexFixed = 0x29800000, - LoadStorePairPreIndexFMask = 0x3B800000, - LoadStorePairPreIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ - A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) - #undef LOAD_STORE_PAIR_PRE_INDEX -}; - -enum LoadStorePairOffsetOp { - LoadStorePairOffsetFixed = 0x29000000, - LoadStorePairOffsetFMask = 0x3B800000, - LoadStorePairOffsetMask = 0xFFC00000, - #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ - A##_##B##_off = LoadStorePairOffsetFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) - #undef LOAD_STORE_PAIR_OFFSET -}; - -enum LoadStorePairNonTemporalOp { - LoadStorePairNonTemporalFixed = 0x28000000, - LoadStorePairNonTemporalFMask = 0x3B800000, - LoadStorePairNonTemporalMask = 0xFFC00000, - LoadStorePairNonTemporalLBit = 1 << 22, - STNP_w = LoadStorePairNonTemporalFixed | STP_w, - LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, - STNP_x = LoadStorePairNonTemporalFixed | STP_x, - LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, - STNP_s = LoadStorePairNonTemporalFixed | STP_s, - LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, - STNP_d = LoadStorePairNonTemporalFixed | STP_d, - LDNP_d = LoadStorePairNonTemporalFixed | LDP_d, - STNP_q = LoadStorePairNonTemporalFixed | STP_q, - LDNP_q = LoadStorePairNonTemporalFixed | LDP_q -}; - -// Load literal. -enum LoadLiteralOp { - LoadLiteralFixed = 0x18000000, - LoadLiteralFMask = 0x3B000000, - LoadLiteralMask = 0xFF000000, - LDR_w_lit = LoadLiteralFixed | 0x00000000, - LDR_x_lit = LoadLiteralFixed | 0x40000000, - LDRSW_x_lit = LoadLiteralFixed | 0x80000000, - PRFM_lit = LoadLiteralFixed | 0xC0000000, - LDR_s_lit = LoadLiteralFixed | 0x04000000, - LDR_d_lit = LoadLiteralFixed | 0x44000000, - LDR_q_lit = LoadLiteralFixed | 0x84000000 -}; - -#define LOAD_STORE_OP_LIST(V) \ - V(ST, RB, w, 0x00000000), \ - V(ST, RH, w, 0x40000000), \ - V(ST, R, w, 0x80000000), \ - V(ST, R, x, 0xC0000000), \ - V(LD, RB, w, 0x00400000), \ - V(LD, RH, w, 0x40400000), \ - V(LD, R, w, 0x80400000), \ - V(LD, R, x, 0xC0400000), \ - V(LD, RSB, x, 0x00800000), \ - V(LD, RSH, x, 0x40800000), \ - V(LD, RSW, x, 0x80800000), \ - V(LD, RSB, w, 0x00C00000), \ - V(LD, RSH, w, 0x40C00000), \ - V(ST, R, b, 0x04000000), \ - V(ST, R, h, 0x44000000), \ - V(ST, R, s, 0x84000000), \ - V(ST, R, d, 0xC4000000), \ - V(ST, R, q, 0x04800000), \ - V(LD, R, b, 0x04400000), \ - V(LD, R, h, 0x44400000), \ - V(LD, R, s, 0x84400000), \ - V(LD, R, d, 0xC4400000), \ - V(LD, R, q, 0x04C00000) - -// Load/store (post, pre, offset and unsigned.) -enum LoadStoreOp { - LoadStoreMask = 0xC4C00000, - LoadStoreVMask = 0x04000000, - #define LOAD_STORE(A, B, C, D) \ - A##B##_##C = D - LOAD_STORE_OP_LIST(LOAD_STORE), - #undef LOAD_STORE - PRFM = 0xC0800000 -}; - -// Load/store unscaled offset. -enum LoadStoreUnscaledOffsetOp { - LoadStoreUnscaledOffsetFixed = 0x38000000, - LoadStoreUnscaledOffsetFMask = 0x3B200C00, - LoadStoreUnscaledOffsetMask = 0xFFE00C00, - PRFUM = LoadStoreUnscaledOffsetFixed | PRFM, - #define LOAD_STORE_UNSCALED(A, B, C, D) \ - A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) - #undef LOAD_STORE_UNSCALED -}; - -// Load/store post index. -enum LoadStorePostIndex { - LoadStorePostIndexFixed = 0x38000400, - LoadStorePostIndexFMask = 0x3B200C00, - LoadStorePostIndexMask = 0xFFE00C00, - #define LOAD_STORE_POST_INDEX(A, B, C, D) \ - A##B##_##C##_post = LoadStorePostIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) - #undef LOAD_STORE_POST_INDEX -}; - -// Load/store pre index. -enum LoadStorePreIndex { - LoadStorePreIndexFixed = 0x38000C00, - LoadStorePreIndexFMask = 0x3B200C00, - LoadStorePreIndexMask = 0xFFE00C00, - #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ - A##B##_##C##_pre = LoadStorePreIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) - #undef LOAD_STORE_PRE_INDEX -}; - -// Load/store unsigned offset. -enum LoadStoreUnsignedOffset { - LoadStoreUnsignedOffsetFixed = 0x39000000, - LoadStoreUnsignedOffsetFMask = 0x3B000000, - LoadStoreUnsignedOffsetMask = 0xFFC00000, - PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, - #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ - A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) - #undef LOAD_STORE_UNSIGNED_OFFSET -}; - -// Load/store register offset. -enum LoadStoreRegisterOffset { - LoadStoreRegisterOffsetFixed = 0x38200800, - LoadStoreRegisterOffsetFMask = 0x3B200C00, - LoadStoreRegisterOffsetMask = 0xFFE00C00, - PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, - #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ - A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) - #undef LOAD_STORE_REGISTER_OFFSET -}; - -enum LoadStoreExclusive { - LoadStoreExclusiveFixed = 0x08000000, - LoadStoreExclusiveFMask = 0x3F000000, - LoadStoreExclusiveMask = 0xFFE08000, - STXRB_w = LoadStoreExclusiveFixed | 0x00000000, - STXRH_w = LoadStoreExclusiveFixed | 0x40000000, - STXR_w = LoadStoreExclusiveFixed | 0x80000000, - STXR_x = LoadStoreExclusiveFixed | 0xC0000000, - LDXRB_w = LoadStoreExclusiveFixed | 0x00400000, - LDXRH_w = LoadStoreExclusiveFixed | 0x40400000, - LDXR_w = LoadStoreExclusiveFixed | 0x80400000, - LDXR_x = LoadStoreExclusiveFixed | 0xC0400000, - STXP_w = LoadStoreExclusiveFixed | 0x80200000, - STXP_x = LoadStoreExclusiveFixed | 0xC0200000, - LDXP_w = LoadStoreExclusiveFixed | 0x80600000, - LDXP_x = LoadStoreExclusiveFixed | 0xC0600000, - STLXRB_w = LoadStoreExclusiveFixed | 0x00008000, - STLXRH_w = LoadStoreExclusiveFixed | 0x40008000, - STLXR_w = LoadStoreExclusiveFixed | 0x80008000, - STLXR_x = LoadStoreExclusiveFixed | 0xC0008000, - LDAXRB_w = LoadStoreExclusiveFixed | 0x00408000, - LDAXRH_w = LoadStoreExclusiveFixed | 0x40408000, - LDAXR_w = LoadStoreExclusiveFixed | 0x80408000, - LDAXR_x = LoadStoreExclusiveFixed | 0xC0408000, - STLXP_w = LoadStoreExclusiveFixed | 0x80208000, - STLXP_x = LoadStoreExclusiveFixed | 0xC0208000, - LDAXP_w = LoadStoreExclusiveFixed | 0x80608000, - LDAXP_x = LoadStoreExclusiveFixed | 0xC0608000, - STLRB_w = LoadStoreExclusiveFixed | 0x00808000, - STLRH_w = LoadStoreExclusiveFixed | 0x40808000, - STLR_w = LoadStoreExclusiveFixed | 0x80808000, - STLR_x = LoadStoreExclusiveFixed | 0xC0808000, - LDARB_w = LoadStoreExclusiveFixed | 0x00C08000, - LDARH_w = LoadStoreExclusiveFixed | 0x40C08000, - LDAR_w = LoadStoreExclusiveFixed | 0x80C08000, - LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000 -}; - -// Conditional compare. -enum ConditionalCompareOp { - ConditionalCompareMask = 0x60000000, - CCMN = 0x20000000, - CCMP = 0x60000000 -}; - -// Conditional compare register. -enum ConditionalCompareRegisterOp { - ConditionalCompareRegisterFixed = 0x1A400000, - ConditionalCompareRegisterFMask = 0x1FE00800, - ConditionalCompareRegisterMask = 0xFFE00C10, - CCMN_w = ConditionalCompareRegisterFixed | CCMN, - CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, - CCMP_w = ConditionalCompareRegisterFixed | CCMP, - CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP -}; - -// Conditional compare immediate. -enum ConditionalCompareImmediateOp { - ConditionalCompareImmediateFixed = 0x1A400800, - ConditionalCompareImmediateFMask = 0x1FE00800, - ConditionalCompareImmediateMask = 0xFFE00C10, - CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, - CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, - CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, - CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP -}; - -// Conditional select. -enum ConditionalSelectOp { - ConditionalSelectFixed = 0x1A800000, - ConditionalSelectFMask = 0x1FE00000, - ConditionalSelectMask = 0xFFE00C00, - CSEL_w = ConditionalSelectFixed | 0x00000000, - CSEL_x = ConditionalSelectFixed | 0x80000000, - CSEL = CSEL_w, - CSINC_w = ConditionalSelectFixed | 0x00000400, - CSINC_x = ConditionalSelectFixed | 0x80000400, - CSINC = CSINC_w, - CSINV_w = ConditionalSelectFixed | 0x40000000, - CSINV_x = ConditionalSelectFixed | 0xC0000000, - CSINV = CSINV_w, - CSNEG_w = ConditionalSelectFixed | 0x40000400, - CSNEG_x = ConditionalSelectFixed | 0xC0000400, - CSNEG = CSNEG_w -}; - -// Data processing 1 source. -enum DataProcessing1SourceOp { - DataProcessing1SourceFixed = 0x5AC00000, - DataProcessing1SourceFMask = 0x5FE00000, - DataProcessing1SourceMask = 0xFFFFFC00, - RBIT = DataProcessing1SourceFixed | 0x00000000, - RBIT_w = RBIT, - RBIT_x = RBIT | SixtyFourBits, - REV16 = DataProcessing1SourceFixed | 0x00000400, - REV16_w = REV16, - REV16_x = REV16 | SixtyFourBits, - REV = DataProcessing1SourceFixed | 0x00000800, - REV_w = REV, - REV32_x = REV | SixtyFourBits, - REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, - CLZ = DataProcessing1SourceFixed | 0x00001000, - CLZ_w = CLZ, - CLZ_x = CLZ | SixtyFourBits, - CLS = DataProcessing1SourceFixed | 0x00001400, - CLS_w = CLS, - CLS_x = CLS | SixtyFourBits -}; - -// Data processing 2 source. -enum DataProcessing2SourceOp { - DataProcessing2SourceFixed = 0x1AC00000, - DataProcessing2SourceFMask = 0x5FE00000, - DataProcessing2SourceMask = 0xFFE0FC00, - UDIV_w = DataProcessing2SourceFixed | 0x00000800, - UDIV_x = DataProcessing2SourceFixed | 0x80000800, - UDIV = UDIV_w, - SDIV_w = DataProcessing2SourceFixed | 0x00000C00, - SDIV_x = DataProcessing2SourceFixed | 0x80000C00, - SDIV = SDIV_w, - LSLV_w = DataProcessing2SourceFixed | 0x00002000, - LSLV_x = DataProcessing2SourceFixed | 0x80002000, - LSLV = LSLV_w, - LSRV_w = DataProcessing2SourceFixed | 0x00002400, - LSRV_x = DataProcessing2SourceFixed | 0x80002400, - LSRV = LSRV_w, - ASRV_w = DataProcessing2SourceFixed | 0x00002800, - ASRV_x = DataProcessing2SourceFixed | 0x80002800, - ASRV = ASRV_w, - RORV_w = DataProcessing2SourceFixed | 0x00002C00, - RORV_x = DataProcessing2SourceFixed | 0x80002C00, - RORV = RORV_w, - CRC32B = DataProcessing2SourceFixed | 0x00004000, - CRC32H = DataProcessing2SourceFixed | 0x00004400, - CRC32W = DataProcessing2SourceFixed | 0x00004800, - CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, - CRC32CB = DataProcessing2SourceFixed | 0x00005000, - CRC32CH = DataProcessing2SourceFixed | 0x00005400, - CRC32CW = DataProcessing2SourceFixed | 0x00005800, - CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 -}; - -// Data processing 3 source. -enum DataProcessing3SourceOp { - DataProcessing3SourceFixed = 0x1B000000, - DataProcessing3SourceFMask = 0x1F000000, - DataProcessing3SourceMask = 0xFFE08000, - MADD_w = DataProcessing3SourceFixed | 0x00000000, - MADD_x = DataProcessing3SourceFixed | 0x80000000, - MADD = MADD_w, - MSUB_w = DataProcessing3SourceFixed | 0x00008000, - MSUB_x = DataProcessing3SourceFixed | 0x80008000, - MSUB = MSUB_w, - SMADDL_x = DataProcessing3SourceFixed | 0x80200000, - SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, - SMULH_x = DataProcessing3SourceFixed | 0x80400000, - UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, - UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, - UMULH_x = DataProcessing3SourceFixed | 0x80C00000 -}; - -// Floating point compare. -enum FPCompareOp { - FPCompareFixed = 0x1E202000, - FPCompareFMask = 0x5F203C00, - FPCompareMask = 0xFFE0FC1F, - FCMP_s = FPCompareFixed | 0x00000000, - FCMP_d = FPCompareFixed | FP64 | 0x00000000, - FCMP = FCMP_s, - FCMP_s_zero = FPCompareFixed | 0x00000008, - FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, - FCMP_zero = FCMP_s_zero, - FCMPE_s = FPCompareFixed | 0x00000010, - FCMPE_d = FPCompareFixed | FP64 | 0x00000010, - FCMPE = FCMPE_s, - FCMPE_s_zero = FPCompareFixed | 0x00000018, - FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018, - FCMPE_zero = FCMPE_s_zero -}; - -// Floating point conditional compare. -enum FPConditionalCompareOp { - FPConditionalCompareFixed = 0x1E200400, - FPConditionalCompareFMask = 0x5F200C00, - FPConditionalCompareMask = 0xFFE00C10, - FCCMP_s = FPConditionalCompareFixed | 0x00000000, - FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, - FCCMP = FCCMP_s, - FCCMPE_s = FPConditionalCompareFixed | 0x00000010, - FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, - FCCMPE = FCCMPE_s -}; - -// Floating point conditional select. -enum FPConditionalSelectOp { - FPConditionalSelectFixed = 0x1E200C00, - FPConditionalSelectFMask = 0x5F200C00, - FPConditionalSelectMask = 0xFFE00C00, - FCSEL_s = FPConditionalSelectFixed | 0x00000000, - FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, - FCSEL = FCSEL_s -}; - -// Floating point immediate. -enum FPImmediateOp { - FPImmediateFixed = 0x1E201000, - FPImmediateFMask = 0x5F201C00, - FPImmediateMask = 0xFFE01C00, - FMOV_s_imm = FPImmediateFixed | 0x00000000, - FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 -}; - -// Floating point data processing 1 source. -enum FPDataProcessing1SourceOp { - FPDataProcessing1SourceFixed = 0x1E204000, - FPDataProcessing1SourceFMask = 0x5F207C00, - FPDataProcessing1SourceMask = 0xFFFFFC00, - FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, - FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, - FMOV = FMOV_s, - FABS_s = FPDataProcessing1SourceFixed | 0x00008000, - FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, - FABS = FABS_s, - FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, - FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, - FNEG = FNEG_s, - FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, - FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, - FSQRT = FSQRT_s, - FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, - FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, - FCVT_hs = FPDataProcessing1SourceFixed | 0x00038000, - FCVT_hd = FPDataProcessing1SourceFixed | FP64 | 0x00038000, - FCVT_sh = FPDataProcessing1SourceFixed | 0x00C20000, - FCVT_dh = FPDataProcessing1SourceFixed | 0x00C28000, - FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, - FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, - FRINTN = FRINTN_s, - FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, - FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, - FRINTP = FRINTP_s, - FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, - FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, - FRINTM = FRINTM_s, - FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, - FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, - FRINTZ = FRINTZ_s, - FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, - FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, - FRINTA = FRINTA_s, - FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, - FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, - FRINTX = FRINTX_s, - FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, - FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000, - FRINTI = FRINTI_s -}; - -// Floating point data processing 2 source. -enum FPDataProcessing2SourceOp { - FPDataProcessing2SourceFixed = 0x1E200800, - FPDataProcessing2SourceFMask = 0x5F200C00, - FPDataProcessing2SourceMask = 0xFFE0FC00, - FMUL = FPDataProcessing2SourceFixed | 0x00000000, - FMUL_s = FMUL, - FMUL_d = FMUL | FP64, - FDIV = FPDataProcessing2SourceFixed | 0x00001000, - FDIV_s = FDIV, - FDIV_d = FDIV | FP64, - FADD = FPDataProcessing2SourceFixed | 0x00002000, - FADD_s = FADD, - FADD_d = FADD | FP64, - FSUB = FPDataProcessing2SourceFixed | 0x00003000, - FSUB_s = FSUB, - FSUB_d = FSUB | FP64, - FMAX = FPDataProcessing2SourceFixed | 0x00004000, - FMAX_s = FMAX, - FMAX_d = FMAX | FP64, - FMIN = FPDataProcessing2SourceFixed | 0x00005000, - FMIN_s = FMIN, - FMIN_d = FMIN | FP64, - FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, - FMAXNM_s = FMAXNM, - FMAXNM_d = FMAXNM | FP64, - FMINNM = FPDataProcessing2SourceFixed | 0x00007000, - FMINNM_s = FMINNM, - FMINNM_d = FMINNM | FP64, - FNMUL = FPDataProcessing2SourceFixed | 0x00008000, - FNMUL_s = FNMUL, - FNMUL_d = FNMUL | FP64 -}; - -// Floating point data processing 3 source. -enum FPDataProcessing3SourceOp { - FPDataProcessing3SourceFixed = 0x1F000000, - FPDataProcessing3SourceFMask = 0x5F000000, - FPDataProcessing3SourceMask = 0xFFE08000, - FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, - FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, - FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, - FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, - FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, - FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, - FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, - FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 -}; - -// Conversion between floating point and integer. -enum FPIntegerConvertOp { - FPIntegerConvertFixed = 0x1E200000, - FPIntegerConvertFMask = 0x5F20FC00, - FPIntegerConvertMask = 0xFFFFFC00, - FCVTNS = FPIntegerConvertFixed | 0x00000000, - FCVTNS_ws = FCVTNS, - FCVTNS_xs = FCVTNS | SixtyFourBits, - FCVTNS_wd = FCVTNS | FP64, - FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, - FCVTNU = FPIntegerConvertFixed | 0x00010000, - FCVTNU_ws = FCVTNU, - FCVTNU_xs = FCVTNU | SixtyFourBits, - FCVTNU_wd = FCVTNU | FP64, - FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, - FCVTPS = FPIntegerConvertFixed | 0x00080000, - FCVTPS_ws = FCVTPS, - FCVTPS_xs = FCVTPS | SixtyFourBits, - FCVTPS_wd = FCVTPS | FP64, - FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, - FCVTPU = FPIntegerConvertFixed | 0x00090000, - FCVTPU_ws = FCVTPU, - FCVTPU_xs = FCVTPU | SixtyFourBits, - FCVTPU_wd = FCVTPU | FP64, - FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, - FCVTMS = FPIntegerConvertFixed | 0x00100000, - FCVTMS_ws = FCVTMS, - FCVTMS_xs = FCVTMS | SixtyFourBits, - FCVTMS_wd = FCVTMS | FP64, - FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, - FCVTMU = FPIntegerConvertFixed | 0x00110000, - FCVTMU_ws = FCVTMU, - FCVTMU_xs = FCVTMU | SixtyFourBits, - FCVTMU_wd = FCVTMU | FP64, - FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, - FCVTZS = FPIntegerConvertFixed | 0x00180000, - FCVTZS_ws = FCVTZS, - FCVTZS_xs = FCVTZS | SixtyFourBits, - FCVTZS_wd = FCVTZS | FP64, - FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, - FCVTZU = FPIntegerConvertFixed | 0x00190000, - FCVTZU_ws = FCVTZU, - FCVTZU_xs = FCVTZU | SixtyFourBits, - FCVTZU_wd = FCVTZU | FP64, - FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, - SCVTF = FPIntegerConvertFixed | 0x00020000, - SCVTF_sw = SCVTF, - SCVTF_sx = SCVTF | SixtyFourBits, - SCVTF_dw = SCVTF | FP64, - SCVTF_dx = SCVTF | SixtyFourBits | FP64, - UCVTF = FPIntegerConvertFixed | 0x00030000, - UCVTF_sw = UCVTF, - UCVTF_sx = UCVTF | SixtyFourBits, - UCVTF_dw = UCVTF | FP64, - UCVTF_dx = UCVTF | SixtyFourBits | FP64, - FCVTAS = FPIntegerConvertFixed | 0x00040000, - FCVTAS_ws = FCVTAS, - FCVTAS_xs = FCVTAS | SixtyFourBits, - FCVTAS_wd = FCVTAS | FP64, - FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, - FCVTAU = FPIntegerConvertFixed | 0x00050000, - FCVTAU_ws = FCVTAU, - FCVTAU_xs = FCVTAU | SixtyFourBits, - FCVTAU_wd = FCVTAU | FP64, - FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, - FMOV_ws = FPIntegerConvertFixed | 0x00060000, - FMOV_sw = FPIntegerConvertFixed | 0x00070000, - FMOV_xd = FMOV_ws | SixtyFourBits | FP64, - FMOV_dx = FMOV_sw | SixtyFourBits | FP64, - FMOV_d1_x = FPIntegerConvertFixed | SixtyFourBits | 0x008F0000, - FMOV_x_d1 = FPIntegerConvertFixed | SixtyFourBits | 0x008E0000 -}; - -// Conversion between fixed point and floating point. -enum FPFixedPointConvertOp { - FPFixedPointConvertFixed = 0x1E000000, - FPFixedPointConvertFMask = 0x5F200000, - FPFixedPointConvertMask = 0xFFFF0000, - FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, - FCVTZS_ws_fixed = FCVTZS_fixed, - FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, - FCVTZS_wd_fixed = FCVTZS_fixed | FP64, - FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, - FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, - FCVTZU_ws_fixed = FCVTZU_fixed, - FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, - FCVTZU_wd_fixed = FCVTZU_fixed | FP64, - FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, - SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, - SCVTF_sw_fixed = SCVTF_fixed, - SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, - SCVTF_dw_fixed = SCVTF_fixed | FP64, - SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, - UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, - UCVTF_sw_fixed = UCVTF_fixed, - UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, - UCVTF_dw_fixed = UCVTF_fixed | FP64, - UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 -}; - -// Crypto - two register SHA. -enum Crypto2RegSHAOp { - Crypto2RegSHAFixed = 0x5E280800, - Crypto2RegSHAFMask = 0xFF3E0C00 -}; - -// Crypto - three register SHA. -enum Crypto3RegSHAOp { - Crypto3RegSHAFixed = 0x5E000000, - Crypto3RegSHAFMask = 0xFF208C00 -}; - -// Crypto - AES. -enum CryptoAESOp { - CryptoAESFixed = 0x4E280800, - CryptoAESFMask = 0xFF3E0C00 -}; - -// NEON instructions with two register operands. -enum NEON2RegMiscOp { - NEON2RegMiscFixed = 0x0E200800, - NEON2RegMiscFMask = 0x9F3E0C00, - NEON2RegMiscMask = 0xBF3FFC00, - NEON2RegMiscUBit = 0x20000000, - NEON_REV64 = NEON2RegMiscFixed | 0x00000000, - NEON_REV32 = NEON2RegMiscFixed | 0x20000000, - NEON_REV16 = NEON2RegMiscFixed | 0x00001000, - NEON_SADDLP = NEON2RegMiscFixed | 0x00002000, - NEON_UADDLP = NEON_SADDLP | NEON2RegMiscUBit, - NEON_SUQADD = NEON2RegMiscFixed | 0x00003000, - NEON_USQADD = NEON_SUQADD | NEON2RegMiscUBit, - NEON_CLS = NEON2RegMiscFixed | 0x00004000, - NEON_CLZ = NEON2RegMiscFixed | 0x20004000, - NEON_CNT = NEON2RegMiscFixed | 0x00005000, - NEON_RBIT_NOT = NEON2RegMiscFixed | 0x20005000, - NEON_SADALP = NEON2RegMiscFixed | 0x00006000, - NEON_UADALP = NEON_SADALP | NEON2RegMiscUBit, - NEON_SQABS = NEON2RegMiscFixed | 0x00007000, - NEON_SQNEG = NEON2RegMiscFixed | 0x20007000, - NEON_CMGT_zero = NEON2RegMiscFixed | 0x00008000, - NEON_CMGE_zero = NEON2RegMiscFixed | 0x20008000, - NEON_CMEQ_zero = NEON2RegMiscFixed | 0x00009000, - NEON_CMLE_zero = NEON2RegMiscFixed | 0x20009000, - NEON_CMLT_zero = NEON2RegMiscFixed | 0x0000A000, - NEON_ABS = NEON2RegMiscFixed | 0x0000B000, - NEON_NEG = NEON2RegMiscFixed | 0x2000B000, - NEON_XTN = NEON2RegMiscFixed | 0x00012000, - NEON_SQXTUN = NEON2RegMiscFixed | 0x20012000, - NEON_SHLL = NEON2RegMiscFixed | 0x20013000, - NEON_SQXTN = NEON2RegMiscFixed | 0x00014000, - NEON_UQXTN = NEON_SQXTN | NEON2RegMiscUBit, - - NEON2RegMiscOpcode = 0x0001F000, - NEON_RBIT_NOT_opcode = NEON_RBIT_NOT & NEON2RegMiscOpcode, - NEON_NEG_opcode = NEON_NEG & NEON2RegMiscOpcode, - NEON_XTN_opcode = NEON_XTN & NEON2RegMiscOpcode, - NEON_UQXTN_opcode = NEON_UQXTN & NEON2RegMiscOpcode, - - // These instructions use only one bit of the size field. The other bit is - // used to distinguish between instructions. - NEON2RegMiscFPMask = NEON2RegMiscMask | 0x00800000, - NEON_FABS = NEON2RegMiscFixed | 0x0080F000, - NEON_FNEG = NEON2RegMiscFixed | 0x2080F000, - NEON_FCVTN = NEON2RegMiscFixed | 0x00016000, - NEON_FCVTXN = NEON2RegMiscFixed | 0x20016000, - NEON_FCVTL = NEON2RegMiscFixed | 0x00017000, - NEON_FRINTN = NEON2RegMiscFixed | 0x00018000, - NEON_FRINTA = NEON2RegMiscFixed | 0x20018000, - NEON_FRINTP = NEON2RegMiscFixed | 0x00818000, - NEON_FRINTM = NEON2RegMiscFixed | 0x00019000, - NEON_FRINTX = NEON2RegMiscFixed | 0x20019000, - NEON_FRINTZ = NEON2RegMiscFixed | 0x00819000, - NEON_FRINTI = NEON2RegMiscFixed | 0x20819000, - NEON_FCVTNS = NEON2RegMiscFixed | 0x0001A000, - NEON_FCVTNU = NEON_FCVTNS | NEON2RegMiscUBit, - NEON_FCVTPS = NEON2RegMiscFixed | 0x0081A000, - NEON_FCVTPU = NEON_FCVTPS | NEON2RegMiscUBit, - NEON_FCVTMS = NEON2RegMiscFixed | 0x0001B000, - NEON_FCVTMU = NEON_FCVTMS | NEON2RegMiscUBit, - NEON_FCVTZS = NEON2RegMiscFixed | 0x0081B000, - NEON_FCVTZU = NEON_FCVTZS | NEON2RegMiscUBit, - NEON_FCVTAS = NEON2RegMiscFixed | 0x0001C000, - NEON_FCVTAU = NEON_FCVTAS | NEON2RegMiscUBit, - NEON_FSQRT = NEON2RegMiscFixed | 0x2081F000, - NEON_SCVTF = NEON2RegMiscFixed | 0x0001D000, - NEON_UCVTF = NEON_SCVTF | NEON2RegMiscUBit, - NEON_URSQRTE = NEON2RegMiscFixed | 0x2081C000, - NEON_URECPE = NEON2RegMiscFixed | 0x0081C000, - NEON_FRSQRTE = NEON2RegMiscFixed | 0x2081D000, - NEON_FRECPE = NEON2RegMiscFixed | 0x0081D000, - NEON_FCMGT_zero = NEON2RegMiscFixed | 0x0080C000, - NEON_FCMGE_zero = NEON2RegMiscFixed | 0x2080C000, - NEON_FCMEQ_zero = NEON2RegMiscFixed | 0x0080D000, - NEON_FCMLE_zero = NEON2RegMiscFixed | 0x2080D000, - NEON_FCMLT_zero = NEON2RegMiscFixed | 0x0080E000, - - NEON_FCVTL_opcode = NEON_FCVTL & NEON2RegMiscOpcode, - NEON_FCVTN_opcode = NEON_FCVTN & NEON2RegMiscOpcode -}; - -// NEON instructions with three same-type operands. -enum NEON3SameOp { - NEON3SameFixed = 0x0E200400, - NEON3SameFMask = 0x9F200400, - NEON3SameMask = 0xBF20FC00, - NEON3SameUBit = 0x20000000, - NEON_ADD = NEON3SameFixed | 0x00008000, - NEON_ADDP = NEON3SameFixed | 0x0000B800, - NEON_SHADD = NEON3SameFixed | 0x00000000, - NEON_SHSUB = NEON3SameFixed | 0x00002000, - NEON_SRHADD = NEON3SameFixed | 0x00001000, - NEON_CMEQ = NEON3SameFixed | NEON3SameUBit | 0x00008800, - NEON_CMGE = NEON3SameFixed | 0x00003800, - NEON_CMGT = NEON3SameFixed | 0x00003000, - NEON_CMHI = NEON3SameFixed | NEON3SameUBit | NEON_CMGT, - NEON_CMHS = NEON3SameFixed | NEON3SameUBit | NEON_CMGE, - NEON_CMTST = NEON3SameFixed | 0x00008800, - NEON_MLA = NEON3SameFixed | 0x00009000, - NEON_MLS = NEON3SameFixed | 0x20009000, - NEON_MUL = NEON3SameFixed | 0x00009800, - NEON_PMUL = NEON3SameFixed | 0x20009800, - NEON_SRSHL = NEON3SameFixed | 0x00005000, - NEON_SQSHL = NEON3SameFixed | 0x00004800, - NEON_SQRSHL = NEON3SameFixed | 0x00005800, - NEON_SSHL = NEON3SameFixed | 0x00004000, - NEON_SMAX = NEON3SameFixed | 0x00006000, - NEON_SMAXP = NEON3SameFixed | 0x0000A000, - NEON_SMIN = NEON3SameFixed | 0x00006800, - NEON_SMINP = NEON3SameFixed | 0x0000A800, - NEON_SABD = NEON3SameFixed | 0x00007000, - NEON_SABA = NEON3SameFixed | 0x00007800, - NEON_UABD = NEON3SameFixed | NEON3SameUBit | NEON_SABD, - NEON_UABA = NEON3SameFixed | NEON3SameUBit | NEON_SABA, - NEON_SQADD = NEON3SameFixed | 0x00000800, - NEON_SQSUB = NEON3SameFixed | 0x00002800, - NEON_SUB = NEON3SameFixed | NEON3SameUBit | 0x00008000, - NEON_UHADD = NEON3SameFixed | NEON3SameUBit | NEON_SHADD, - NEON_UHSUB = NEON3SameFixed | NEON3SameUBit | NEON_SHSUB, - NEON_URHADD = NEON3SameFixed | NEON3SameUBit | NEON_SRHADD, - NEON_UMAX = NEON3SameFixed | NEON3SameUBit | NEON_SMAX, - NEON_UMAXP = NEON3SameFixed | NEON3SameUBit | NEON_SMAXP, - NEON_UMIN = NEON3SameFixed | NEON3SameUBit | NEON_SMIN, - NEON_UMINP = NEON3SameFixed | NEON3SameUBit | NEON_SMINP, - NEON_URSHL = NEON3SameFixed | NEON3SameUBit | NEON_SRSHL, - NEON_UQADD = NEON3SameFixed | NEON3SameUBit | NEON_SQADD, - NEON_UQRSHL = NEON3SameFixed | NEON3SameUBit | NEON_SQRSHL, - NEON_UQSHL = NEON3SameFixed | NEON3SameUBit | NEON_SQSHL, - NEON_UQSUB = NEON3SameFixed | NEON3SameUBit | NEON_SQSUB, - NEON_USHL = NEON3SameFixed | NEON3SameUBit | NEON_SSHL, - NEON_SQDMULH = NEON3SameFixed | 0x0000B000, - NEON_SQRDMULH = NEON3SameFixed | 0x2000B000, - - // NEON floating point instructions with three same-type operands. - NEON3SameFPFixed = NEON3SameFixed | 0x0000C000, - NEON3SameFPFMask = NEON3SameFMask | 0x0000C000, - NEON3SameFPMask = NEON3SameMask | 0x00800000, - NEON_FADD = NEON3SameFixed | 0x0000D000, - NEON_FSUB = NEON3SameFixed | 0x0080D000, - NEON_FMUL = NEON3SameFixed | 0x2000D800, - NEON_FDIV = NEON3SameFixed | 0x2000F800, - NEON_FMAX = NEON3SameFixed | 0x0000F000, - NEON_FMAXNM = NEON3SameFixed | 0x0000C000, - NEON_FMAXP = NEON3SameFixed | 0x2000F000, - NEON_FMAXNMP = NEON3SameFixed | 0x2000C000, - NEON_FMIN = NEON3SameFixed | 0x0080F000, - NEON_FMINNM = NEON3SameFixed | 0x0080C000, - NEON_FMINP = NEON3SameFixed | 0x2080F000, - NEON_FMINNMP = NEON3SameFixed | 0x2080C000, - NEON_FMLA = NEON3SameFixed | 0x0000C800, - NEON_FMLS = NEON3SameFixed | 0x0080C800, - NEON_FMULX = NEON3SameFixed | 0x0000D800, - NEON_FRECPS = NEON3SameFixed | 0x0000F800, - NEON_FRSQRTS = NEON3SameFixed | 0x0080F800, - NEON_FABD = NEON3SameFixed | 0x2080D000, - NEON_FADDP = NEON3SameFixed | 0x2000D000, - NEON_FCMEQ = NEON3SameFixed | 0x0000E000, - NEON_FCMGE = NEON3SameFixed | 0x2000E000, - NEON_FCMGT = NEON3SameFixed | 0x2080E000, - NEON_FACGE = NEON3SameFixed | 0x2000E800, - NEON_FACGT = NEON3SameFixed | 0x2080E800, - - // NEON logical instructions with three same-type operands. - NEON3SameLogicalFixed = NEON3SameFixed | 0x00001800, - NEON3SameLogicalFMask = NEON3SameFMask | 0x0000F800, - NEON3SameLogicalMask = 0xBFE0FC00, - NEON3SameLogicalFormatMask = NEON_Q, - NEON_AND = NEON3SameLogicalFixed | 0x00000000, - NEON_ORR = NEON3SameLogicalFixed | 0x00A00000, - NEON_ORN = NEON3SameLogicalFixed | 0x00C00000, - NEON_EOR = NEON3SameLogicalFixed | 0x20000000, - NEON_BIC = NEON3SameLogicalFixed | 0x00400000, - NEON_BIF = NEON3SameLogicalFixed | 0x20C00000, - NEON_BIT = NEON3SameLogicalFixed | 0x20800000, - NEON_BSL = NEON3SameLogicalFixed | 0x20400000 -}; - -// NEON instructions with three different-type operands. -enum NEON3DifferentOp { - NEON3DifferentFixed = 0x0E200000, - NEON3DifferentFMask = 0x9F200C00, - NEON3DifferentMask = 0xFF20FC00, - NEON_ADDHN = NEON3DifferentFixed | 0x00004000, - NEON_ADDHN2 = NEON_ADDHN | NEON_Q, - NEON_PMULL = NEON3DifferentFixed | 0x0000E000, - NEON_PMULL2 = NEON_PMULL | NEON_Q, - NEON_RADDHN = NEON3DifferentFixed | 0x20004000, - NEON_RADDHN2 = NEON_RADDHN | NEON_Q, - NEON_RSUBHN = NEON3DifferentFixed | 0x20006000, - NEON_RSUBHN2 = NEON_RSUBHN | NEON_Q, - NEON_SABAL = NEON3DifferentFixed | 0x00005000, - NEON_SABAL2 = NEON_SABAL | NEON_Q, - NEON_SABDL = NEON3DifferentFixed | 0x00007000, - NEON_SABDL2 = NEON_SABDL | NEON_Q, - NEON_SADDL = NEON3DifferentFixed | 0x00000000, - NEON_SADDL2 = NEON_SADDL | NEON_Q, - NEON_SADDW = NEON3DifferentFixed | 0x00001000, - NEON_SADDW2 = NEON_SADDW | NEON_Q, - NEON_SMLAL = NEON3DifferentFixed | 0x00008000, - NEON_SMLAL2 = NEON_SMLAL | NEON_Q, - NEON_SMLSL = NEON3DifferentFixed | 0x0000A000, - NEON_SMLSL2 = NEON_SMLSL | NEON_Q, - NEON_SMULL = NEON3DifferentFixed | 0x0000C000, - NEON_SMULL2 = NEON_SMULL | NEON_Q, - NEON_SSUBL = NEON3DifferentFixed | 0x00002000, - NEON_SSUBL2 = NEON_SSUBL | NEON_Q, - NEON_SSUBW = NEON3DifferentFixed | 0x00003000, - NEON_SSUBW2 = NEON_SSUBW | NEON_Q, - NEON_SQDMLAL = NEON3DifferentFixed | 0x00009000, - NEON_SQDMLAL2 = NEON_SQDMLAL | NEON_Q, - NEON_SQDMLSL = NEON3DifferentFixed | 0x0000B000, - NEON_SQDMLSL2 = NEON_SQDMLSL | NEON_Q, - NEON_SQDMULL = NEON3DifferentFixed | 0x0000D000, - NEON_SQDMULL2 = NEON_SQDMULL | NEON_Q, - NEON_SUBHN = NEON3DifferentFixed | 0x00006000, - NEON_SUBHN2 = NEON_SUBHN | NEON_Q, - NEON_UABAL = NEON_SABAL | NEON3SameUBit, - NEON_UABAL2 = NEON_UABAL | NEON_Q, - NEON_UABDL = NEON_SABDL | NEON3SameUBit, - NEON_UABDL2 = NEON_UABDL | NEON_Q, - NEON_UADDL = NEON_SADDL | NEON3SameUBit, - NEON_UADDL2 = NEON_UADDL | NEON_Q, - NEON_UADDW = NEON_SADDW | NEON3SameUBit, - NEON_UADDW2 = NEON_UADDW | NEON_Q, - NEON_UMLAL = NEON_SMLAL | NEON3SameUBit, - NEON_UMLAL2 = NEON_UMLAL | NEON_Q, - NEON_UMLSL = NEON_SMLSL | NEON3SameUBit, - NEON_UMLSL2 = NEON_UMLSL | NEON_Q, - NEON_UMULL = NEON_SMULL | NEON3SameUBit, - NEON_UMULL2 = NEON_UMULL | NEON_Q, - NEON_USUBL = NEON_SSUBL | NEON3SameUBit, - NEON_USUBL2 = NEON_USUBL | NEON_Q, - NEON_USUBW = NEON_SSUBW | NEON3SameUBit, - NEON_USUBW2 = NEON_USUBW | NEON_Q -}; - -// NEON instructions operating across vectors. -enum NEONAcrossLanesOp { - NEONAcrossLanesFixed = 0x0E300800, - NEONAcrossLanesFMask = 0x9F3E0C00, - NEONAcrossLanesMask = 0xBF3FFC00, - NEON_ADDV = NEONAcrossLanesFixed | 0x0001B000, - NEON_SADDLV = NEONAcrossLanesFixed | 0x00003000, - NEON_UADDLV = NEONAcrossLanesFixed | 0x20003000, - NEON_SMAXV = NEONAcrossLanesFixed | 0x0000A000, - NEON_SMINV = NEONAcrossLanesFixed | 0x0001A000, - NEON_UMAXV = NEONAcrossLanesFixed | 0x2000A000, - NEON_UMINV = NEONAcrossLanesFixed | 0x2001A000, - - // NEON floating point across instructions. - NEONAcrossLanesFPFixed = NEONAcrossLanesFixed | 0x0000C000, - NEONAcrossLanesFPFMask = NEONAcrossLanesFMask | 0x0000C000, - NEONAcrossLanesFPMask = NEONAcrossLanesMask | 0x00800000, - - NEON_FMAXV = NEONAcrossLanesFPFixed | 0x2000F000, - NEON_FMINV = NEONAcrossLanesFPFixed | 0x2080F000, - NEON_FMAXNMV = NEONAcrossLanesFPFixed | 0x2000C000, - NEON_FMINNMV = NEONAcrossLanesFPFixed | 0x2080C000 -}; - -// NEON instructions with indexed element operand. -enum NEONByIndexedElementOp { - NEONByIndexedElementFixed = 0x0F000000, - NEONByIndexedElementFMask = 0x9F000400, - NEONByIndexedElementMask = 0xBF00F400, - NEON_MUL_byelement = NEONByIndexedElementFixed | 0x00008000, - NEON_MLA_byelement = NEONByIndexedElementFixed | 0x20000000, - NEON_MLS_byelement = NEONByIndexedElementFixed | 0x20004000, - NEON_SMULL_byelement = NEONByIndexedElementFixed | 0x0000A000, - NEON_SMLAL_byelement = NEONByIndexedElementFixed | 0x00002000, - NEON_SMLSL_byelement = NEONByIndexedElementFixed | 0x00006000, - NEON_UMULL_byelement = NEONByIndexedElementFixed | 0x2000A000, - NEON_UMLAL_byelement = NEONByIndexedElementFixed | 0x20002000, - NEON_UMLSL_byelement = NEONByIndexedElementFixed | 0x20006000, - NEON_SQDMULL_byelement = NEONByIndexedElementFixed | 0x0000B000, - NEON_SQDMLAL_byelement = NEONByIndexedElementFixed | 0x00003000, - NEON_SQDMLSL_byelement = NEONByIndexedElementFixed | 0x00007000, - NEON_SQDMULH_byelement = NEONByIndexedElementFixed | 0x0000C000, - NEON_SQRDMULH_byelement = NEONByIndexedElementFixed | 0x0000D000, - - // Floating point instructions. - NEONByIndexedElementFPFixed = NEONByIndexedElementFixed | 0x00800000, - NEONByIndexedElementFPMask = NEONByIndexedElementMask | 0x00800000, - NEON_FMLA_byelement = NEONByIndexedElementFPFixed | 0x00001000, - NEON_FMLS_byelement = NEONByIndexedElementFPFixed | 0x00005000, - NEON_FMUL_byelement = NEONByIndexedElementFPFixed | 0x00009000, - NEON_FMULX_byelement = NEONByIndexedElementFPFixed | 0x20009000 -}; - -// NEON register copy. -enum NEONCopyOp { - NEONCopyFixed = 0x0E000400, - NEONCopyFMask = 0x9FE08400, - NEONCopyMask = 0x3FE08400, - NEONCopyInsElementMask = NEONCopyMask | 0x40000000, - NEONCopyInsGeneralMask = NEONCopyMask | 0x40007800, - NEONCopyDupElementMask = NEONCopyMask | 0x20007800, - NEONCopyDupGeneralMask = NEONCopyDupElementMask, - NEONCopyUmovMask = NEONCopyMask | 0x20007800, - NEONCopySmovMask = NEONCopyMask | 0x20007800, - NEON_INS_ELEMENT = NEONCopyFixed | 0x60000000, - NEON_INS_GENERAL = NEONCopyFixed | 0x40001800, - NEON_DUP_ELEMENT = NEONCopyFixed | 0x00000000, - NEON_DUP_GENERAL = NEONCopyFixed | 0x00000800, - NEON_SMOV = NEONCopyFixed | 0x00002800, - NEON_UMOV = NEONCopyFixed | 0x00003800 -}; - -// NEON extract. -enum NEONExtractOp { - NEONExtractFixed = 0x2E000000, - NEONExtractFMask = 0xBF208400, - NEONExtractMask = 0xBFE08400, - NEON_EXT = NEONExtractFixed | 0x00000000 -}; - -enum NEONLoadStoreMultiOp { - NEONLoadStoreMultiL = 0x00400000, - NEONLoadStoreMulti1_1v = 0x00007000, - NEONLoadStoreMulti1_2v = 0x0000A000, - NEONLoadStoreMulti1_3v = 0x00006000, - NEONLoadStoreMulti1_4v = 0x00002000, - NEONLoadStoreMulti2 = 0x00008000, - NEONLoadStoreMulti3 = 0x00004000, - NEONLoadStoreMulti4 = 0x00000000 -}; - -// NEON load/store multiple structures. -enum NEONLoadStoreMultiStructOp { - NEONLoadStoreMultiStructFixed = 0x0C000000, - NEONLoadStoreMultiStructFMask = 0xBFBF0000, - NEONLoadStoreMultiStructMask = 0xBFFFF000, - NEONLoadStoreMultiStructStore = NEONLoadStoreMultiStructFixed, - NEONLoadStoreMultiStructLoad = NEONLoadStoreMultiStructFixed | - NEONLoadStoreMultiL, - NEON_LD1_1v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_1v, - NEON_LD1_2v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_2v, - NEON_LD1_3v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_3v, - NEON_LD1_4v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_4v, - NEON_LD2 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti2, - NEON_LD3 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti3, - NEON_LD4 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti4, - NEON_ST1_1v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_1v, - NEON_ST1_2v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_2v, - NEON_ST1_3v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_3v, - NEON_ST1_4v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_4v, - NEON_ST2 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti2, - NEON_ST3 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti3, - NEON_ST4 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti4 -}; - -// NEON load/store multiple structures with post-index addressing. -enum NEONLoadStoreMultiStructPostIndexOp { - NEONLoadStoreMultiStructPostIndexFixed = 0x0C800000, - NEONLoadStoreMultiStructPostIndexFMask = 0xBFA00000, - NEONLoadStoreMultiStructPostIndexMask = 0xBFE0F000, - NEONLoadStoreMultiStructPostIndex = 0x00800000, - NEON_LD1_1v_post = NEON_LD1_1v | NEONLoadStoreMultiStructPostIndex, - NEON_LD1_2v_post = NEON_LD1_2v | NEONLoadStoreMultiStructPostIndex, - NEON_LD1_3v_post = NEON_LD1_3v | NEONLoadStoreMultiStructPostIndex, - NEON_LD1_4v_post = NEON_LD1_4v | NEONLoadStoreMultiStructPostIndex, - NEON_LD2_post = NEON_LD2 | NEONLoadStoreMultiStructPostIndex, - NEON_LD3_post = NEON_LD3 | NEONLoadStoreMultiStructPostIndex, - NEON_LD4_post = NEON_LD4 | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_1v_post = NEON_ST1_1v | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_2v_post = NEON_ST1_2v | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_3v_post = NEON_ST1_3v | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_4v_post = NEON_ST1_4v | NEONLoadStoreMultiStructPostIndex, - NEON_ST2_post = NEON_ST2 | NEONLoadStoreMultiStructPostIndex, - NEON_ST3_post = NEON_ST3 | NEONLoadStoreMultiStructPostIndex, - NEON_ST4_post = NEON_ST4 | NEONLoadStoreMultiStructPostIndex -}; - -enum NEONLoadStoreSingleOp { - NEONLoadStoreSingle1 = 0x00000000, - NEONLoadStoreSingle2 = 0x00200000, - NEONLoadStoreSingle3 = 0x00002000, - NEONLoadStoreSingle4 = 0x00202000, - NEONLoadStoreSingleL = 0x00400000, - NEONLoadStoreSingle_b = 0x00000000, - NEONLoadStoreSingle_h = 0x00004000, - NEONLoadStoreSingle_s = 0x00008000, - NEONLoadStoreSingle_d = 0x00008400, - NEONLoadStoreSingleAllLanes = 0x0000C000, - NEONLoadStoreSingleLenMask = 0x00202000 -}; - -// NEON load/store single structure. -enum NEONLoadStoreSingleStructOp { - NEONLoadStoreSingleStructFixed = 0x0D000000, - NEONLoadStoreSingleStructFMask = 0xBF9F0000, - NEONLoadStoreSingleStructMask = 0xBFFFE000, - NEONLoadStoreSingleStructStore = NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructLoad = NEONLoadStoreSingleStructFixed | - NEONLoadStoreSingleL, - NEONLoadStoreSingleStructLoad1 = NEONLoadStoreSingle1 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructLoad2 = NEONLoadStoreSingle2 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructLoad3 = NEONLoadStoreSingle3 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructLoad4 = NEONLoadStoreSingle4 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructStore1 = NEONLoadStoreSingle1 | - NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructStore2 = NEONLoadStoreSingle2 | - NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructStore3 = NEONLoadStoreSingle3 | - NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructStore4 = NEONLoadStoreSingle4 | - NEONLoadStoreSingleStructFixed, - NEON_LD1_b = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_b, - NEON_LD1_h = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_h, - NEON_LD1_s = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_s, - NEON_LD1_d = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_d, - NEON_LD1R = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingleAllLanes, - NEON_ST1_b = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_b, - NEON_ST1_h = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_h, - NEON_ST1_s = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_s, - NEON_ST1_d = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_d, - - NEON_LD2_b = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_b, - NEON_LD2_h = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_h, - NEON_LD2_s = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_s, - NEON_LD2_d = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_d, - NEON_LD2R = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingleAllLanes, - NEON_ST2_b = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_b, - NEON_ST2_h = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_h, - NEON_ST2_s = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_s, - NEON_ST2_d = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_d, - - NEON_LD3_b = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_b, - NEON_LD3_h = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_h, - NEON_LD3_s = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_s, - NEON_LD3_d = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_d, - NEON_LD3R = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingleAllLanes, - NEON_ST3_b = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_b, - NEON_ST3_h = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_h, - NEON_ST3_s = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_s, - NEON_ST3_d = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_d, - - NEON_LD4_b = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_b, - NEON_LD4_h = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_h, - NEON_LD4_s = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_s, - NEON_LD4_d = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_d, - NEON_LD4R = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingleAllLanes, - NEON_ST4_b = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_b, - NEON_ST4_h = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_h, - NEON_ST4_s = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_s, - NEON_ST4_d = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_d -}; - -// NEON load/store single structure with post-index addressing. -enum NEONLoadStoreSingleStructPostIndexOp { - NEONLoadStoreSingleStructPostIndexFixed = 0x0D800000, - NEONLoadStoreSingleStructPostIndexFMask = 0xBF800000, - NEONLoadStoreSingleStructPostIndexMask = 0xBFE0E000, - NEONLoadStoreSingleStructPostIndex = 0x00800000, - NEON_LD1_b_post = NEON_LD1_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD1_h_post = NEON_LD1_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD1_s_post = NEON_LD1_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD1_d_post = NEON_LD1_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD1R_post = NEON_LD1R | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_b_post = NEON_ST1_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_h_post = NEON_ST1_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_s_post = NEON_ST1_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_d_post = NEON_ST1_d | NEONLoadStoreSingleStructPostIndex, - - NEON_LD2_b_post = NEON_LD2_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD2_h_post = NEON_LD2_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD2_s_post = NEON_LD2_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD2_d_post = NEON_LD2_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD2R_post = NEON_LD2R | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_b_post = NEON_ST2_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_h_post = NEON_ST2_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_s_post = NEON_ST2_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_d_post = NEON_ST2_d | NEONLoadStoreSingleStructPostIndex, - - NEON_LD3_b_post = NEON_LD3_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD3_h_post = NEON_LD3_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD3_s_post = NEON_LD3_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD3_d_post = NEON_LD3_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD3R_post = NEON_LD3R | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_b_post = NEON_ST3_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_h_post = NEON_ST3_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_s_post = NEON_ST3_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_d_post = NEON_ST3_d | NEONLoadStoreSingleStructPostIndex, - - NEON_LD4_b_post = NEON_LD4_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD4_h_post = NEON_LD4_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD4_s_post = NEON_LD4_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD4_d_post = NEON_LD4_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD4R_post = NEON_LD4R | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_b_post = NEON_ST4_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_h_post = NEON_ST4_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_s_post = NEON_ST4_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_d_post = NEON_ST4_d | NEONLoadStoreSingleStructPostIndex -}; - -// NEON modified immediate. -enum NEONModifiedImmediateOp { - NEONModifiedImmediateFixed = 0x0F000400, - NEONModifiedImmediateFMask = 0x9FF80400, - NEONModifiedImmediateOpBit = 0x20000000, - NEONModifiedImmediate_MOVI = NEONModifiedImmediateFixed | 0x00000000, - NEONModifiedImmediate_MVNI = NEONModifiedImmediateFixed | 0x20000000, - NEONModifiedImmediate_ORR = NEONModifiedImmediateFixed | 0x00001000, - NEONModifiedImmediate_BIC = NEONModifiedImmediateFixed | 0x20001000 -}; - -// NEON shift immediate. -enum NEONShiftImmediateOp { - NEONShiftImmediateFixed = 0x0F000400, - NEONShiftImmediateFMask = 0x9F800400, - NEONShiftImmediateMask = 0xBF80FC00, - NEONShiftImmediateUBit = 0x20000000, - NEON_SHL = NEONShiftImmediateFixed | 0x00005000, - NEON_SSHLL = NEONShiftImmediateFixed | 0x0000A000, - NEON_USHLL = NEONShiftImmediateFixed | 0x2000A000, - NEON_SLI = NEONShiftImmediateFixed | 0x20005000, - NEON_SRI = NEONShiftImmediateFixed | 0x20004000, - NEON_SHRN = NEONShiftImmediateFixed | 0x00008000, - NEON_RSHRN = NEONShiftImmediateFixed | 0x00008800, - NEON_UQSHRN = NEONShiftImmediateFixed | 0x20009000, - NEON_UQRSHRN = NEONShiftImmediateFixed | 0x20009800, - NEON_SQSHRN = NEONShiftImmediateFixed | 0x00009000, - NEON_SQRSHRN = NEONShiftImmediateFixed | 0x00009800, - NEON_SQSHRUN = NEONShiftImmediateFixed | 0x20008000, - NEON_SQRSHRUN = NEONShiftImmediateFixed | 0x20008800, - NEON_SSHR = NEONShiftImmediateFixed | 0x00000000, - NEON_SRSHR = NEONShiftImmediateFixed | 0x00002000, - NEON_USHR = NEONShiftImmediateFixed | 0x20000000, - NEON_URSHR = NEONShiftImmediateFixed | 0x20002000, - NEON_SSRA = NEONShiftImmediateFixed | 0x00001000, - NEON_SRSRA = NEONShiftImmediateFixed | 0x00003000, - NEON_USRA = NEONShiftImmediateFixed | 0x20001000, - NEON_URSRA = NEONShiftImmediateFixed | 0x20003000, - NEON_SQSHLU = NEONShiftImmediateFixed | 0x20006000, - NEON_SCVTF_imm = NEONShiftImmediateFixed | 0x0000E000, - NEON_UCVTF_imm = NEONShiftImmediateFixed | 0x2000E000, - NEON_FCVTZS_imm = NEONShiftImmediateFixed | 0x0000F800, - NEON_FCVTZU_imm = NEONShiftImmediateFixed | 0x2000F800, - NEON_SQSHL_imm = NEONShiftImmediateFixed | 0x00007000, - NEON_UQSHL_imm = NEONShiftImmediateFixed | 0x20007000 -}; - -// NEON table. -enum NEONTableOp { - NEONTableFixed = 0x0E000000, - NEONTableFMask = 0xBF208C00, - NEONTableExt = 0x00001000, - NEONTableMask = 0xBF20FC00, - NEON_TBL_1v = NEONTableFixed | 0x00000000, - NEON_TBL_2v = NEONTableFixed | 0x00002000, - NEON_TBL_3v = NEONTableFixed | 0x00004000, - NEON_TBL_4v = NEONTableFixed | 0x00006000, - NEON_TBX_1v = NEON_TBL_1v | NEONTableExt, - NEON_TBX_2v = NEON_TBL_2v | NEONTableExt, - NEON_TBX_3v = NEON_TBL_3v | NEONTableExt, - NEON_TBX_4v = NEON_TBL_4v | NEONTableExt -}; - -// NEON perm. -enum NEONPermOp { - NEONPermFixed = 0x0E000800, - NEONPermFMask = 0xBF208C00, - NEONPermMask = 0x3F20FC00, - NEON_UZP1 = NEONPermFixed | 0x00001000, - NEON_TRN1 = NEONPermFixed | 0x00002000, - NEON_ZIP1 = NEONPermFixed | 0x00003000, - NEON_UZP2 = NEONPermFixed | 0x00005000, - NEON_TRN2 = NEONPermFixed | 0x00006000, - NEON_ZIP2 = NEONPermFixed | 0x00007000 -}; - -// NEON scalar instructions with two register operands. -enum NEONScalar2RegMiscOp { - NEONScalar2RegMiscFixed = 0x5E200800, - NEONScalar2RegMiscFMask = 0xDF3E0C00, - NEONScalar2RegMiscMask = NEON_Q | NEONScalar | NEON2RegMiscMask, - NEON_CMGT_zero_scalar = NEON_Q | NEONScalar | NEON_CMGT_zero, - NEON_CMEQ_zero_scalar = NEON_Q | NEONScalar | NEON_CMEQ_zero, - NEON_CMLT_zero_scalar = NEON_Q | NEONScalar | NEON_CMLT_zero, - NEON_CMGE_zero_scalar = NEON_Q | NEONScalar | NEON_CMGE_zero, - NEON_CMLE_zero_scalar = NEON_Q | NEONScalar | NEON_CMLE_zero, - NEON_ABS_scalar = NEON_Q | NEONScalar | NEON_ABS, - NEON_SQABS_scalar = NEON_Q | NEONScalar | NEON_SQABS, - NEON_NEG_scalar = NEON_Q | NEONScalar | NEON_NEG, - NEON_SQNEG_scalar = NEON_Q | NEONScalar | NEON_SQNEG, - NEON_SQXTN_scalar = NEON_Q | NEONScalar | NEON_SQXTN, - NEON_UQXTN_scalar = NEON_Q | NEONScalar | NEON_UQXTN, - NEON_SQXTUN_scalar = NEON_Q | NEONScalar | NEON_SQXTUN, - NEON_SUQADD_scalar = NEON_Q | NEONScalar | NEON_SUQADD, - NEON_USQADD_scalar = NEON_Q | NEONScalar | NEON_USQADD, - - NEONScalar2RegMiscOpcode = NEON2RegMiscOpcode, - NEON_NEG_scalar_opcode = NEON_NEG_scalar & NEONScalar2RegMiscOpcode, - - NEONScalar2RegMiscFPMask = NEONScalar2RegMiscMask | 0x00800000, - NEON_FRSQRTE_scalar = NEON_Q | NEONScalar | NEON_FRSQRTE, - NEON_FRECPE_scalar = NEON_Q | NEONScalar | NEON_FRECPE, - NEON_SCVTF_scalar = NEON_Q | NEONScalar | NEON_SCVTF, - NEON_UCVTF_scalar = NEON_Q | NEONScalar | NEON_UCVTF, - NEON_FCMGT_zero_scalar = NEON_Q | NEONScalar | NEON_FCMGT_zero, - NEON_FCMEQ_zero_scalar = NEON_Q | NEONScalar | NEON_FCMEQ_zero, - NEON_FCMLT_zero_scalar = NEON_Q | NEONScalar | NEON_FCMLT_zero, - NEON_FCMGE_zero_scalar = NEON_Q | NEONScalar | NEON_FCMGE_zero, - NEON_FCMLE_zero_scalar = NEON_Q | NEONScalar | NEON_FCMLE_zero, - NEON_FRECPX_scalar = NEONScalar2RegMiscFixed | 0x0081F000, - NEON_FCVTNS_scalar = NEON_Q | NEONScalar | NEON_FCVTNS, - NEON_FCVTNU_scalar = NEON_Q | NEONScalar | NEON_FCVTNU, - NEON_FCVTPS_scalar = NEON_Q | NEONScalar | NEON_FCVTPS, - NEON_FCVTPU_scalar = NEON_Q | NEONScalar | NEON_FCVTPU, - NEON_FCVTMS_scalar = NEON_Q | NEONScalar | NEON_FCVTMS, - NEON_FCVTMU_scalar = NEON_Q | NEONScalar | NEON_FCVTMU, - NEON_FCVTZS_scalar = NEON_Q | NEONScalar | NEON_FCVTZS, - NEON_FCVTZU_scalar = NEON_Q | NEONScalar | NEON_FCVTZU, - NEON_FCVTAS_scalar = NEON_Q | NEONScalar | NEON_FCVTAS, - NEON_FCVTAU_scalar = NEON_Q | NEONScalar | NEON_FCVTAU, - NEON_FCVTXN_scalar = NEON_Q | NEONScalar | NEON_FCVTXN -}; - -// NEON scalar instructions with three same-type operands. -enum NEONScalar3SameOp { - NEONScalar3SameFixed = 0x5E200400, - NEONScalar3SameFMask = 0xDF200400, - NEONScalar3SameMask = 0xFF20FC00, - NEON_ADD_scalar = NEON_Q | NEONScalar | NEON_ADD, - NEON_CMEQ_scalar = NEON_Q | NEONScalar | NEON_CMEQ, - NEON_CMGE_scalar = NEON_Q | NEONScalar | NEON_CMGE, - NEON_CMGT_scalar = NEON_Q | NEONScalar | NEON_CMGT, - NEON_CMHI_scalar = NEON_Q | NEONScalar | NEON_CMHI, - NEON_CMHS_scalar = NEON_Q | NEONScalar | NEON_CMHS, - NEON_CMTST_scalar = NEON_Q | NEONScalar | NEON_CMTST, - NEON_SUB_scalar = NEON_Q | NEONScalar | NEON_SUB, - NEON_UQADD_scalar = NEON_Q | NEONScalar | NEON_UQADD, - NEON_SQADD_scalar = NEON_Q | NEONScalar | NEON_SQADD, - NEON_UQSUB_scalar = NEON_Q | NEONScalar | NEON_UQSUB, - NEON_SQSUB_scalar = NEON_Q | NEONScalar | NEON_SQSUB, - NEON_USHL_scalar = NEON_Q | NEONScalar | NEON_USHL, - NEON_SSHL_scalar = NEON_Q | NEONScalar | NEON_SSHL, - NEON_UQSHL_scalar = NEON_Q | NEONScalar | NEON_UQSHL, - NEON_SQSHL_scalar = NEON_Q | NEONScalar | NEON_SQSHL, - NEON_URSHL_scalar = NEON_Q | NEONScalar | NEON_URSHL, - NEON_SRSHL_scalar = NEON_Q | NEONScalar | NEON_SRSHL, - NEON_UQRSHL_scalar = NEON_Q | NEONScalar | NEON_UQRSHL, - NEON_SQRSHL_scalar = NEON_Q | NEONScalar | NEON_SQRSHL, - NEON_SQDMULH_scalar = NEON_Q | NEONScalar | NEON_SQDMULH, - NEON_SQRDMULH_scalar = NEON_Q | NEONScalar | NEON_SQRDMULH, - - // NEON floating point scalar instructions with three same-type operands. - NEONScalar3SameFPFixed = NEONScalar3SameFixed | 0x0000C000, - NEONScalar3SameFPFMask = NEONScalar3SameFMask | 0x0000C000, - NEONScalar3SameFPMask = NEONScalar3SameMask | 0x00800000, - NEON_FACGE_scalar = NEON_Q | NEONScalar | NEON_FACGE, - NEON_FACGT_scalar = NEON_Q | NEONScalar | NEON_FACGT, - NEON_FCMEQ_scalar = NEON_Q | NEONScalar | NEON_FCMEQ, - NEON_FCMGE_scalar = NEON_Q | NEONScalar | NEON_FCMGE, - NEON_FCMGT_scalar = NEON_Q | NEONScalar | NEON_FCMGT, - NEON_FMULX_scalar = NEON_Q | NEONScalar | NEON_FMULX, - NEON_FRECPS_scalar = NEON_Q | NEONScalar | NEON_FRECPS, - NEON_FRSQRTS_scalar = NEON_Q | NEONScalar | NEON_FRSQRTS, - NEON_FABD_scalar = NEON_Q | NEONScalar | NEON_FABD -}; - -// NEON scalar instructions with three different-type operands. -enum NEONScalar3DiffOp { - NEONScalar3DiffFixed = 0x5E200000, - NEONScalar3DiffFMask = 0xDF200C00, - NEONScalar3DiffMask = NEON_Q | NEONScalar | NEON3DifferentMask, - NEON_SQDMLAL_scalar = NEON_Q | NEONScalar | NEON_SQDMLAL, - NEON_SQDMLSL_scalar = NEON_Q | NEONScalar | NEON_SQDMLSL, - NEON_SQDMULL_scalar = NEON_Q | NEONScalar | NEON_SQDMULL -}; - -// NEON scalar instructions with indexed element operand. -enum NEONScalarByIndexedElementOp { - NEONScalarByIndexedElementFixed = 0x5F000000, - NEONScalarByIndexedElementFMask = 0xDF000400, - NEONScalarByIndexedElementMask = 0xFF00F400, - NEON_SQDMLAL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMLAL_byelement, - NEON_SQDMLSL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMLSL_byelement, - NEON_SQDMULL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMULL_byelement, - NEON_SQDMULH_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMULH_byelement, - NEON_SQRDMULH_byelement_scalar - = NEON_Q | NEONScalar | NEON_SQRDMULH_byelement, - - // Floating point instructions. - NEONScalarByIndexedElementFPFixed - = NEONScalarByIndexedElementFixed | 0x00800000, - NEONScalarByIndexedElementFPMask - = NEONScalarByIndexedElementMask | 0x00800000, - NEON_FMLA_byelement_scalar = NEON_Q | NEONScalar | NEON_FMLA_byelement, - NEON_FMLS_byelement_scalar = NEON_Q | NEONScalar | NEON_FMLS_byelement, - NEON_FMUL_byelement_scalar = NEON_Q | NEONScalar | NEON_FMUL_byelement, - NEON_FMULX_byelement_scalar = NEON_Q | NEONScalar | NEON_FMULX_byelement -}; - -// NEON scalar register copy. -enum NEONScalarCopyOp { - NEONScalarCopyFixed = 0x5E000400, - NEONScalarCopyFMask = 0xDFE08400, - NEONScalarCopyMask = 0xFFE0FC00, - NEON_DUP_ELEMENT_scalar = NEON_Q | NEONScalar | NEON_DUP_ELEMENT -}; - -// NEON scalar pairwise instructions. -enum NEONScalarPairwiseOp { - NEONScalarPairwiseFixed = 0x5E300800, - NEONScalarPairwiseFMask = 0xDF3E0C00, - NEONScalarPairwiseMask = 0xFFB1F800, - NEON_ADDP_scalar = NEONScalarPairwiseFixed | 0x0081B000, - NEON_FMAXNMP_scalar = NEONScalarPairwiseFixed | 0x2000C000, - NEON_FMINNMP_scalar = NEONScalarPairwiseFixed | 0x2080C000, - NEON_FADDP_scalar = NEONScalarPairwiseFixed | 0x2000D000, - NEON_FMAXP_scalar = NEONScalarPairwiseFixed | 0x2000F000, - NEON_FMINP_scalar = NEONScalarPairwiseFixed | 0x2080F000 -}; - -// NEON scalar shift immediate. -enum NEONScalarShiftImmediateOp { - NEONScalarShiftImmediateFixed = 0x5F000400, - NEONScalarShiftImmediateFMask = 0xDF800400, - NEONScalarShiftImmediateMask = 0xFF80FC00, - NEON_SHL_scalar = NEON_Q | NEONScalar | NEON_SHL, - NEON_SLI_scalar = NEON_Q | NEONScalar | NEON_SLI, - NEON_SRI_scalar = NEON_Q | NEONScalar | NEON_SRI, - NEON_SSHR_scalar = NEON_Q | NEONScalar | NEON_SSHR, - NEON_USHR_scalar = NEON_Q | NEONScalar | NEON_USHR, - NEON_SRSHR_scalar = NEON_Q | NEONScalar | NEON_SRSHR, - NEON_URSHR_scalar = NEON_Q | NEONScalar | NEON_URSHR, - NEON_SSRA_scalar = NEON_Q | NEONScalar | NEON_SSRA, - NEON_USRA_scalar = NEON_Q | NEONScalar | NEON_USRA, - NEON_SRSRA_scalar = NEON_Q | NEONScalar | NEON_SRSRA, - NEON_URSRA_scalar = NEON_Q | NEONScalar | NEON_URSRA, - NEON_UQSHRN_scalar = NEON_Q | NEONScalar | NEON_UQSHRN, - NEON_UQRSHRN_scalar = NEON_Q | NEONScalar | NEON_UQRSHRN, - NEON_SQSHRN_scalar = NEON_Q | NEONScalar | NEON_SQSHRN, - NEON_SQRSHRN_scalar = NEON_Q | NEONScalar | NEON_SQRSHRN, - NEON_SQSHRUN_scalar = NEON_Q | NEONScalar | NEON_SQSHRUN, - NEON_SQRSHRUN_scalar = NEON_Q | NEONScalar | NEON_SQRSHRUN, - NEON_SQSHLU_scalar = NEON_Q | NEONScalar | NEON_SQSHLU, - NEON_SQSHL_imm_scalar = NEON_Q | NEONScalar | NEON_SQSHL_imm, - NEON_UQSHL_imm_scalar = NEON_Q | NEONScalar | NEON_UQSHL_imm, - NEON_SCVTF_imm_scalar = NEON_Q | NEONScalar | NEON_SCVTF_imm, - NEON_UCVTF_imm_scalar = NEON_Q | NEONScalar | NEON_UCVTF_imm, - NEON_FCVTZS_imm_scalar = NEON_Q | NEONScalar | NEON_FCVTZS_imm, - NEON_FCVTZU_imm_scalar = NEON_Q | NEONScalar | NEON_FCVTZU_imm -}; - -// Unimplemented and unallocated instructions. These are defined to make fixed -// bit assertion easier. -enum UnimplementedOp { - UnimplementedFixed = 0x00000000, - UnimplementedFMask = 0x00000000 -}; - -enum UnallocatedOp { - UnallocatedFixed = 0x00000000, - UnallocatedFMask = 0x00000000 -}; - -} // namespace vixl - -#endif // VIXL_A64_CONSTANTS_A64_H_ diff --git a/disas/libvixl/vixl/a64/cpu-a64.h b/disas/libvixl/vixl/a64/cpu-a64.h deleted file mode 100644 index cdf09a6af17..00000000000 --- a/disas/libvixl/vixl/a64/cpu-a64.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_CPU_A64_H -#define VIXL_CPU_A64_H - -#include "vixl/globals.h" -#include "vixl/a64/instructions-a64.h" - -namespace vixl { - -class CPU { - public: - // Initialise CPU support. - static void SetUp(); - - // Ensures the data at a given address and with a given size is the same for - // the I and D caches. I and D caches are not automatically coherent on ARM - // so this operation is required before any dynamically generated code can - // safely run. - static void EnsureIAndDCacheCoherency(void *address, size_t length); - - // Handle tagged pointers. - template - static T SetPointerTag(T pointer, uint64_t tag) { - VIXL_ASSERT(is_uintn(kAddressTagWidth, tag)); - - // Use C-style casts to get static_cast behaviour for integral types (T), - // and reinterpret_cast behaviour for other types. - - uint64_t raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); - - raw = (raw & ~kAddressTagMask) | (tag << kAddressTagOffset); - return (T)raw; - } - - template - static uint64_t GetPointerTag(T pointer) { - // Use C-style casts to get static_cast behaviour for integral types (T), - // and reinterpret_cast behaviour for other types. - - uint64_t raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); - - return (raw & kAddressTagMask) >> kAddressTagOffset; - } - - private: - // Return the content of the cache type register. - static uint32_t GetCacheType(); - - // I and D cache line size in bytes. - static unsigned icache_line_size_; - static unsigned dcache_line_size_; -}; - -} // namespace vixl - -#endif // VIXL_CPU_A64_H diff --git a/disas/libvixl/vixl/a64/decoder-a64.cc b/disas/libvixl/vixl/a64/decoder-a64.cc deleted file mode 100644 index 5ba2d3ce045..00000000000 --- a/disas/libvixl/vixl/a64/decoder-a64.cc +++ /dev/null @@ -1,877 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "vixl/globals.h" -#include "vixl/utils.h" -#include "vixl/a64/decoder-a64.h" - -namespace vixl { - -void Decoder::DecodeInstruction(const Instruction *instr) { - if (instr->Bits(28, 27) == 0) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(27, 24)) { - // 0: PC relative addressing. - case 0x0: DecodePCRelAddressing(instr); break; - - // 1: Add/sub immediate. - case 0x1: DecodeAddSubImmediate(instr); break; - - // A: Logical shifted register. - // Add/sub with carry. - // Conditional compare register. - // Conditional compare immediate. - // Conditional select. - // Data processing 1 source. - // Data processing 2 source. - // B: Add/sub shifted register. - // Add/sub extended register. - // Data processing 3 source. - case 0xA: - case 0xB: DecodeDataProcessing(instr); break; - - // 2: Logical immediate. - // Move wide immediate. - case 0x2: DecodeLogical(instr); break; - - // 3: Bitfield. - // Extract. - case 0x3: DecodeBitfieldExtract(instr); break; - - // 4: Unconditional branch immediate. - // Exception generation. - // Compare and branch immediate. - // 5: Compare and branch immediate. - // Conditional branch. - // System. - // 6,7: Unconditional branch. - // Test and branch immediate. - case 0x4: - case 0x5: - case 0x6: - case 0x7: DecodeBranchSystemException(instr); break; - - // 8,9: Load/store register pair post-index. - // Load register literal. - // Load/store register unscaled immediate. - // Load/store register immediate post-index. - // Load/store register immediate pre-index. - // Load/store register offset. - // Load/store exclusive. - // C,D: Load/store register pair offset. - // Load/store register pair pre-index. - // Load/store register unsigned immediate. - // Advanced SIMD. - case 0x8: - case 0x9: - case 0xC: - case 0xD: DecodeLoadStore(instr); break; - - // E: FP fixed point conversion. - // FP integer conversion. - // FP data processing 1 source. - // FP compare. - // FP immediate. - // FP data processing 2 source. - // FP conditional compare. - // FP conditional select. - // Advanced SIMD. - // F: FP data processing 3 source. - // Advanced SIMD. - case 0xE: - case 0xF: DecodeFP(instr); break; - } - } -} - -void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { - visitors_.push_back(new_visitor); -} - - -void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { - visitors_.push_front(new_visitor); -} - - -void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.insert(it, new_visitor); -} - - -void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - it++; - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.push_back(new_visitor); -} - - -void Decoder::RemoveVisitor(DecoderVisitor* visitor) { - visitors_.remove(visitor); -} - - -void Decoder::DecodePCRelAddressing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x0); - // We know bit 28 is set, as = 0 is filtered out at the top level - // decode. - VIXL_ASSERT(instr->Bit(28) == 0x1); - VisitPCRelAddressing(instr); -} - - -void Decoder::DecodeBranchSystemException(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0x4) || - (instr->Bits(27, 24) == 0x5) || - (instr->Bits(27, 24) == 0x6) || - (instr->Bits(27, 24) == 0x7) ); - - switch (instr->Bits(31, 29)) { - case 0: - case 4: { - VisitUnconditionalBranch(instr); - break; - } - case 1: - case 5: { - if (instr->Bit(25) == 0) { - VisitCompareBranch(instr); - } else { - VisitTestBranch(instr); - } - break; - } - case 2: { - if (instr->Bit(25) == 0) { - if ((instr->Bit(24) == 0x1) || - (instr->Mask(0x01000010) == 0x00000010)) { - VisitUnallocated(instr); - } else { - VisitConditionalBranch(instr); - } - } else { - VisitUnallocated(instr); - } - break; - } - case 6: { - if (instr->Bit(25) == 0) { - if (instr->Bit(24) == 0) { - if ((instr->Bits(4, 2) != 0) || - (instr->Mask(0x00E0001D) == 0x00200001) || - (instr->Mask(0x00E0001D) == 0x00400001) || - (instr->Mask(0x00E0001E) == 0x00200002) || - (instr->Mask(0x00E0001E) == 0x00400002) || - (instr->Mask(0x00E0001C) == 0x00600000) || - (instr->Mask(0x00E0001C) == 0x00800000) || - (instr->Mask(0x00E0001F) == 0x00A00000) || - (instr->Mask(0x00C0001C) == 0x00C00000)) { - VisitUnallocated(instr); - } else { - VisitException(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); - if ((instr->Bits(21, 19) == 0x4) || - (masked_003FF0E0 == 0x00033000) || - (masked_003FF0E0 == 0x003FF020) || - (masked_003FF0E0 == 0x003FF060) || - (masked_003FF0E0 == 0x003FF0E0) || - (instr->Mask(0x00388000) == 0x00008000) || - (instr->Mask(0x0038E000) == 0x00000000) || - (instr->Mask(0x0039E000) == 0x00002000) || - (instr->Mask(0x003AE000) == 0x00002000) || - (instr->Mask(0x003CE000) == 0x00042000) || - (instr->Mask(0x003FFFC0) == 0x000320C0) || - (instr->Mask(0x003FF100) == 0x00032100) || - (instr->Mask(0x003FF200) == 0x00032200) || - (instr->Mask(0x003FF400) == 0x00032400) || - (instr->Mask(0x003FF800) == 0x00032800) || - (instr->Mask(0x0038F000) == 0x00005000) || - (instr->Mask(0x0038E000) == 0x00006000)) { - VisitUnallocated(instr); - } else { - VisitSystem(instr); - } - } else { - VisitUnallocated(instr); - } - } - } else { - if ((instr->Bit(24) == 0x1) || - (instr->Bits(20, 16) != 0x1F) || - (instr->Bits(15, 10) != 0) || - (instr->Bits(4, 0) != 0) || - (instr->Bits(24, 21) == 0x3) || - (instr->Bits(24, 22) == 0x3)) { - VisitUnallocated(instr); - } else { - VisitUnconditionalBranchToRegister(instr); - } - } - break; - } - case 3: - case 7: { - VisitUnallocated(instr); - break; - } - } -} - - -void Decoder::DecodeLoadStore(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0x8) || - (instr->Bits(27, 24) == 0x9) || - (instr->Bits(27, 24) == 0xC) || - (instr->Bits(27, 24) == 0xD) ); - // TODO(all): rearrange the tree to integrate this branch. - if ((instr->Bit(28) == 0) && (instr->Bit(29) == 0) && (instr->Bit(26) == 1)) { - DecodeNEONLoadStore(instr); - return; - } - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(26) == 0) { - VisitLoadStoreExclusive(instr); - } else { - VIXL_UNREACHABLE(); - } - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - if (instr->Mask(0xC4400000) == 0xC0400000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePairNonTemporal(instr); - } - } else { - VisitLoadStorePairPostIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - if (instr->Mask(0xC4000000) == 0xC4000000) { - VisitUnallocated(instr); - } else { - VisitLoadLiteral(instr); - } - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(21) == 0) { - switch (instr->Bits(11, 10)) { - case 0: { - VisitLoadStoreUnscaledOffset(instr); - break; - } - case 1: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePostIndex(instr); - } - break; - } - case 2: { - // TODO: VisitLoadStoreRegisterOffsetUnpriv. - VisitUnimplemented(instr); - break; - } - case 3: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePreIndex(instr); - } - break; - } - } - } else { - if (instr->Bits(11, 10) == 0x2) { - if (instr->Bit(14) == 0) { - VisitUnallocated(instr); - } else { - VisitLoadStoreRegisterOffset(instr); - } - } else { - VisitUnallocated(instr); - } - } - } - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLoadStorePairOffset(instr); - } else { - VisitLoadStorePairPreIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - VisitLoadStoreUnsignedOffset(instr); - } - } - } - } -} - - -void Decoder::DecodeLogical(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x2); - - if (instr->Mask(0x80400000) == 0x00400000) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLogicalImmediate(instr); - } else { - if (instr->Bits(30, 29) == 0x1) { - VisitUnallocated(instr); - } else { - VisitMoveWideImmediate(instr); - } - } - } -} - - -void Decoder::DecodeBitfieldExtract(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x3); - - if ((instr->Mask(0x80400000) == 0x80000000) || - (instr->Mask(0x80400000) == 0x00400000) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else if (instr->Bit(23) == 0) { - if ((instr->Mask(0x80200000) == 0x00200000) || - (instr->Mask(0x60000000) == 0x60000000)) { - VisitUnallocated(instr); - } else { - VisitBitfield(instr); - } - } else { - if ((instr->Mask(0x60200000) == 0x00200000) || - (instr->Mask(0x60000000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitExtract(instr); - } - } -} - - -void Decoder::DecodeAddSubImmediate(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x1); - if (instr->Bit(23) == 1) { - VisitUnallocated(instr); - } else { - VisitAddSubImmediate(instr); - } -} - - -void Decoder::DecodeDataProcessing(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0xA) || - (instr->Bits(27, 24) == 0xB)); - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Mask(0x80008000) == 0x00008000) { - VisitUnallocated(instr); - } else { - VisitLogicalShifted(instr); - } - } else { - switch (instr->Bits(23, 21)) { - case 0: { - if (instr->Mask(0x0000FC00) != 0) { - VisitUnallocated(instr); - } else { - VisitAddSubWithCarry(instr); - } - break; - } - case 2: { - if ((instr->Bit(29) == 0) || - (instr->Mask(0x00000410) != 0)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(11) == 0) { - VisitConditionalCompareRegister(instr); - } else { - VisitConditionalCompareImmediate(instr); - } - } - break; - } - case 4: { - if (instr->Mask(0x20000800) != 0x00000000) { - VisitUnallocated(instr); - } else { - VisitConditionalSelect(instr); - } - break; - } - case 6: { - if (instr->Bit(29) == 0x1) { - VisitUnallocated(instr); - VIXL_FALLTHROUGH(); - } else { - if (instr->Bit(30) == 0) { - if ((instr->Bit(15) == 0x1) || - (instr->Bits(15, 11) == 0) || - (instr->Bits(15, 12) == 0x1) || - (instr->Bits(15, 12) == 0x3) || - (instr->Bits(15, 13) == 0x3) || - (instr->Mask(0x8000EC00) == 0x00004C00) || - (instr->Mask(0x8000E800) == 0x80004000) || - (instr->Mask(0x8000E400) == 0x80004000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing2Source(instr); - } - } else { - if ((instr->Bit(13) == 1) || - (instr->Bits(20, 16) != 0) || - (instr->Bits(15, 14) != 0) || - (instr->Mask(0xA01FFC00) == 0x00000C00) || - (instr->Mask(0x201FF800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing1Source(instr); - } - } - break; - } - } - case 1: - case 3: - case 5: - case 7: VisitUnallocated(instr); break; - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else { - VisitAddSubShifted(instr); - } - } else { - if ((instr->Mask(0x00C00000) != 0x00000000) || - (instr->Mask(0x00001400) == 0x00001400) || - (instr->Mask(0x00001800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitAddSubExtended(instr); - } - } - } else { - if ((instr->Bit(30) == 0x1) || - (instr->Bits(30, 29) == 0x1) || - (instr->Mask(0xE0600000) == 0x00200000) || - (instr->Mask(0xE0608000) == 0x00400000) || - (instr->Mask(0x60608000) == 0x00408000) || - (instr->Mask(0x60E00000) == 0x00E00000) || - (instr->Mask(0x60E00000) == 0x00800000) || - (instr->Mask(0x60E00000) == 0x00600000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing3Source(instr); - } - } - } -} - - -void Decoder::DecodeFP(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0xE) || - (instr->Bits(27, 24) == 0xF)); - if (instr->Bit(28) == 0) { - DecodeNEONVectorDataProcessing(instr); - } else { - if (instr->Bits(31, 30) == 0x3) { - VisitUnallocated(instr); - } else if (instr->Bits(31, 30) == 0x1) { - DecodeNEONScalarDataProcessing(instr); - } else { - if (instr->Bit(29) == 0) { - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bit(23) == 1) || - (instr->Bit(18) == 1) || - (instr->Mask(0x80008000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x00160000) == 0x00000000) || - (instr->Mask(0x00160000) == 0x00120000)) { - VisitUnallocated(instr); - } else { - VisitFPFixedPointConvert(instr); - } - } else { - if (instr->Bits(15, 10) == 32) { - VisitUnallocated(instr); - } else if (instr->Bits(15, 10) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x000E0000) == 0x000C0000) || - (instr->Mask(0x00160000) == 0x00120000) || - (instr->Mask(0x00160000) == 0x00140000) || - (instr->Mask(0x20C40000) == 0x00800000) || - (instr->Mask(0x20C60000) == 0x00840000) || - (instr->Mask(0xA0C60000) == 0x80060000) || - (instr->Mask(0xA0C60000) == 0x00860000) || - (instr->Mask(0xA0C60000) == 0x00460000) || - (instr->Mask(0xA0CE0000) == 0x80860000) || - (instr->Mask(0xA0CE0000) == 0x804E0000) || - (instr->Mask(0xA0CE0000) == 0x000E0000) || - (instr->Mask(0xA0D60000) == 0x00160000) || - (instr->Mask(0xA0D60000) == 0x80560000) || - (instr->Mask(0xA0D60000) == 0x80960000)) { - VisitUnallocated(instr); - } else { - VisitFPIntegerConvert(instr); - } - } else if (instr->Bits(14, 10) == 16) { - const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); - if ((instr->Mask(0x80180000) != 0) || - (masked_A0DF8000 == 0x00020000) || - (masked_A0DF8000 == 0x00030000) || - (masked_A0DF8000 == 0x00068000) || - (masked_A0DF8000 == 0x00428000) || - (masked_A0DF8000 == 0x00430000) || - (masked_A0DF8000 == 0x00468000) || - (instr->Mask(0xA0D80000) == 0x00800000) || - (instr->Mask(0xA0DE0000) == 0x00C00000) || - (instr->Mask(0xA0DF0000) == 0x00C30000) || - (instr->Mask(0xA0DC0000) == 0x00C40000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing1Source(instr); - } - } else if (instr->Bits(13, 10) == 8) { - if ((instr->Bits(15, 14) != 0) || - (instr->Bits(2, 0) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPCompare(instr); - } - } else if (instr->Bits(12, 10) == 4) { - if ((instr->Bits(9, 5) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPImmediate(instr); - } - } else { - if (instr->Mask(0x80800000) != 0x00000000) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(11, 10)) { - case 1: { - VisitFPConditionalCompare(instr); - break; - } - case 2: { - if ((instr->Bits(15, 14) == 0x3) || - (instr->Mask(0x00009000) == 0x00009000) || - (instr->Mask(0x0000A000) == 0x0000A000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing2Source(instr); - } - break; - } - case 3: { - VisitFPConditionalSelect(instr); - break; - } - default: VIXL_UNREACHABLE(); - } - } - } - } - } else { - // Bit 30 == 1 has been handled earlier. - VIXL_ASSERT(instr->Bit(30) == 0); - if (instr->Mask(0xA0800000) != 0) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing3Source(instr); - } - } - } else { - VisitUnallocated(instr); - } - } - } -} - - -void Decoder::DecodeNEONLoadStore(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(29, 25) == 0x6); - if (instr->Bit(31) == 0) { - if ((instr->Bit(24) == 0) && (instr->Bit(21) == 1)) { - VisitUnallocated(instr); - return; - } - - if (instr->Bit(23) == 0) { - if (instr->Bits(20, 16) == 0) { - if (instr->Bit(24) == 0) { - VisitNEONLoadStoreMultiStruct(instr); - } else { - VisitNEONLoadStoreSingleStruct(instr); - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bit(24) == 0) { - VisitNEONLoadStoreMultiStructPostIndex(instr); - } else { - VisitNEONLoadStoreSingleStructPostIndex(instr); - } - } - } else { - VisitUnallocated(instr); - } -} - - -void Decoder::DecodeNEONVectorDataProcessing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(28, 25) == 0x7); - if (instr->Bit(31) == 0) { - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if (instr->Bit(15) == 0) { - if (instr->Bit(10) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(11) == 0) { - VisitNEONTable(instr); - } else { - VisitNEONPerm(instr); - } - } else { - VisitNEONExtract(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - VisitNEONCopy(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bit(10) == 0) { - if (instr->Bit(11) == 0) { - VisitNEON3Different(instr); - } else { - if (instr->Bits(18, 17) == 0) { - if (instr->Bit(20) == 0) { - if (instr->Bit(19) == 0) { - VisitNEON2RegMisc(instr); - } else { - if (instr->Bits(30, 29) == 0x2) { - VisitCryptoAES(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - if (instr->Bit(19) == 0) { - VisitNEONAcrossLanes(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } - } else { - VisitNEON3Same(instr); - } - } - } else { - if (instr->Bit(10) == 0) { - VisitNEONByIndexedElement(instr); - } else { - if (instr->Bit(23) == 0) { - if (instr->Bits(22, 19) == 0) { - VisitNEONModifiedImmediate(instr); - } else { - VisitNEONShiftImmediate(instr); - } - } else { - VisitUnallocated(instr); - } - } - } - } else { - VisitUnallocated(instr); - } -} - - -void Decoder::DecodeNEONScalarDataProcessing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(28, 25) == 0xF); - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if (instr->Bit(15) == 0) { - if (instr->Bit(10) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(11) == 0) { - VisitCrypto3RegSHA(instr); - } else { - VisitUnallocated(instr); - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - VisitNEONScalarCopy(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bit(10) == 0) { - if (instr->Bit(11) == 0) { - VisitNEONScalar3Diff(instr); - } else { - if (instr->Bits(18, 17) == 0) { - if (instr->Bit(20) == 0) { - if (instr->Bit(19) == 0) { - VisitNEONScalar2RegMisc(instr); - } else { - if (instr->Bit(29) == 0) { - VisitCrypto2RegSHA(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - if (instr->Bit(19) == 0) { - VisitNEONScalarPairwise(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } - } else { - VisitNEONScalar3Same(instr); - } - } - } else { - if (instr->Bit(10) == 0) { - VisitNEONScalarByIndexedElement(instr); - } else { - if (instr->Bit(23) == 0) { - VisitNEONScalarShiftImmediate(instr); - } else { - VisitUnallocated(instr); - } - } - } -} - - -#define DEFINE_VISITOR_CALLERS(A) \ - void Decoder::Visit##A(const Instruction *instr) { \ - VIXL_ASSERT(instr->Mask(A##FMask) == A##Fixed); \ - std::list::iterator it; \ - for (it = visitors_.begin(); it != visitors_.end(); it++) { \ - (*it)->Visit##A(instr); \ - } \ - } -VISITOR_LIST(DEFINE_VISITOR_CALLERS) -#undef DEFINE_VISITOR_CALLERS -} // namespace vixl diff --git a/disas/libvixl/vixl/a64/decoder-a64.h b/disas/libvixl/vixl/a64/decoder-a64.h deleted file mode 100644 index b3f04f68fc5..00000000000 --- a/disas/libvixl/vixl/a64/decoder-a64.h +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_DECODER_A64_H_ -#define VIXL_A64_DECODER_A64_H_ - -#include - -#include "vixl/globals.h" -#include "vixl/a64/instructions-a64.h" - - -// List macro containing all visitors needed by the decoder class. - -#define VISITOR_LIST_THAT_RETURN(V) \ - V(PCRelAddressing) \ - V(AddSubImmediate) \ - V(LogicalImmediate) \ - V(MoveWideImmediate) \ - V(Bitfield) \ - V(Extract) \ - V(UnconditionalBranch) \ - V(UnconditionalBranchToRegister) \ - V(CompareBranch) \ - V(TestBranch) \ - V(ConditionalBranch) \ - V(System) \ - V(Exception) \ - V(LoadStorePairPostIndex) \ - V(LoadStorePairOffset) \ - V(LoadStorePairPreIndex) \ - V(LoadStorePairNonTemporal) \ - V(LoadLiteral) \ - V(LoadStoreUnscaledOffset) \ - V(LoadStorePostIndex) \ - V(LoadStorePreIndex) \ - V(LoadStoreRegisterOffset) \ - V(LoadStoreUnsignedOffset) \ - V(LoadStoreExclusive) \ - V(LogicalShifted) \ - V(AddSubShifted) \ - V(AddSubExtended) \ - V(AddSubWithCarry) \ - V(ConditionalCompareRegister) \ - V(ConditionalCompareImmediate) \ - V(ConditionalSelect) \ - V(DataProcessing1Source) \ - V(DataProcessing2Source) \ - V(DataProcessing3Source) \ - V(FPCompare) \ - V(FPConditionalCompare) \ - V(FPConditionalSelect) \ - V(FPImmediate) \ - V(FPDataProcessing1Source) \ - V(FPDataProcessing2Source) \ - V(FPDataProcessing3Source) \ - V(FPIntegerConvert) \ - V(FPFixedPointConvert) \ - V(Crypto2RegSHA) \ - V(Crypto3RegSHA) \ - V(CryptoAES) \ - V(NEON2RegMisc) \ - V(NEON3Different) \ - V(NEON3Same) \ - V(NEONAcrossLanes) \ - V(NEONByIndexedElement) \ - V(NEONCopy) \ - V(NEONExtract) \ - V(NEONLoadStoreMultiStruct) \ - V(NEONLoadStoreMultiStructPostIndex) \ - V(NEONLoadStoreSingleStruct) \ - V(NEONLoadStoreSingleStructPostIndex) \ - V(NEONModifiedImmediate) \ - V(NEONScalar2RegMisc) \ - V(NEONScalar3Diff) \ - V(NEONScalar3Same) \ - V(NEONScalarByIndexedElement) \ - V(NEONScalarCopy) \ - V(NEONScalarPairwise) \ - V(NEONScalarShiftImmediate) \ - V(NEONShiftImmediate) \ - V(NEONTable) \ - V(NEONPerm) \ - -#define VISITOR_LIST_THAT_DONT_RETURN(V) \ - V(Unallocated) \ - V(Unimplemented) \ - -#define VISITOR_LIST(V) \ - VISITOR_LIST_THAT_RETURN(V) \ - VISITOR_LIST_THAT_DONT_RETURN(V) \ - -namespace vixl { - -// The Visitor interface. Disassembler and simulator (and other tools) -// must provide implementations for all of these functions. -class DecoderVisitor { - public: - enum VisitorConstness { - kConstVisitor, - kNonConstVisitor - }; - explicit DecoderVisitor(VisitorConstness constness = kConstVisitor) - : constness_(constness) {} - - virtual ~DecoderVisitor() {} - - #define DECLARE(A) virtual void Visit##A(const Instruction* instr) = 0; - VISITOR_LIST(DECLARE) - #undef DECLARE - - bool IsConstVisitor() const { return constness_ == kConstVisitor; } - Instruction* MutableInstruction(const Instruction* instr) { - VIXL_ASSERT(!IsConstVisitor()); - return const_cast(instr); - } - - private: - const VisitorConstness constness_; -}; - - -class Decoder { - public: - Decoder() {} - - // Top-level wrappers around the actual decoding function. - void Decode(const Instruction* instr) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - VIXL_ASSERT((*it)->IsConstVisitor()); - } - DecodeInstruction(instr); - } - void Decode(Instruction* instr) { - DecodeInstruction(const_cast(instr)); - } - - // Register a new visitor class with the decoder. - // Decode() will call the corresponding visitor method from all registered - // visitor classes when decoding reaches the leaf node of the instruction - // decode tree. - // Visitors are called in order. - // A visitor can be registered multiple times. - // - // d.AppendVisitor(V1); - // d.AppendVisitor(V2); - // d.PrependVisitor(V2); - // d.AppendVisitor(V3); - // - // d.Decode(i); - // - // will call in order visitor methods in V2, V1, V2, V3. - void AppendVisitor(DecoderVisitor* visitor); - void PrependVisitor(DecoderVisitor* visitor); - // These helpers register `new_visitor` before or after the first instance of - // `registered_visiter` in the list. - // So if - // V1, V2, V1, V2 - // are registered in this order in the decoder, calls to - // d.InsertVisitorAfter(V3, V1); - // d.InsertVisitorBefore(V4, V2); - // will yield the order - // V1, V3, V4, V2, V1, V2 - // - // For more complex modifications of the order of registered visitors, one can - // directly access and modify the list of visitors via the `visitors()' - // accessor. - void InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - void InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - - // Remove all instances of a previously registered visitor class from the list - // of visitors stored by the decoder. - void RemoveVisitor(DecoderVisitor* visitor); - - #define DECLARE(A) void Visit##A(const Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - - std::list* visitors() { return &visitors_; } - - private: - // Decodes an instruction and calls the visitor functions registered with the - // Decoder class. - void DecodeInstruction(const Instruction* instr); - - // Decode the PC relative addressing instruction, and call the corresponding - // visitors. - // On entry, instruction bits 27:24 = 0x0. - void DecodePCRelAddressing(const Instruction* instr); - - // Decode the add/subtract immediate instruction, and call the correspoding - // visitors. - // On entry, instruction bits 27:24 = 0x1. - void DecodeAddSubImmediate(const Instruction* instr); - - // Decode the branch, system command, and exception generation parts of - // the instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. - void DecodeBranchSystemException(const Instruction* instr); - - // Decode the load and store parts of the instruction tree, and call - // the corresponding visitors. - // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. - void DecodeLoadStore(const Instruction* instr); - - // Decode the logical immediate and move wide immediate parts of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x2. - void DecodeLogical(const Instruction* instr); - - // Decode the bitfield and extraction parts of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x3. - void DecodeBitfieldExtract(const Instruction* instr); - - // Decode the data processing parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. - void DecodeDataProcessing(const Instruction* instr); - - // Decode the floating point parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0xE, 0xF}. - void DecodeFP(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 29:25 = 0x6. - void DecodeNEONLoadStore(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) vector data processing part of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 28:25 = 0x7. - void DecodeNEONVectorDataProcessing(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) scalar data processing part of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 28:25 = 0xF. - void DecodeNEONScalarDataProcessing(const Instruction* instr); - - private: - // Visitors are registered in a list. - std::list visitors_; -}; - -} // namespace vixl - -#endif // VIXL_A64_DECODER_A64_H_ diff --git a/disas/libvixl/vixl/a64/disasm-a64.cc b/disas/libvixl/vixl/a64/disasm-a64.cc deleted file mode 100644 index f34d1d68dab..00000000000 --- a/disas/libvixl/vixl/a64/disasm-a64.cc +++ /dev/null @@ -1,3495 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include "vixl/a64/disasm-a64.h" - -namespace vixl { - -Disassembler::Disassembler() { - buffer_size_ = 256; - buffer_ = reinterpret_cast(malloc(buffer_size_)); - buffer_pos_ = 0; - own_buffer_ = true; - code_address_offset_ = 0; -} - - -Disassembler::Disassembler(char* text_buffer, int buffer_size) { - buffer_size_ = buffer_size; - buffer_ = text_buffer; - buffer_pos_ = 0; - own_buffer_ = false; - code_address_offset_ = 0; -} - - -Disassembler::~Disassembler() { - if (own_buffer_) { - free(buffer_); - } -} - - -char* Disassembler::GetOutput() { - return buffer_; -} - - -void Disassembler::VisitAddSubImmediate(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && - (instr->ImmAddSub() == 0) ? true : false; - const char *mnemonic = ""; - const char *form = "'Rds, 'Rns, 'IAddSub"; - const char *form_cmp = "'Rns, 'IAddSub"; - const char *form_mov = "'Rds, 'Rns"; - - switch (instr->Mask(AddSubImmediateMask)) { - case ADD_w_imm: - case ADD_x_imm: { - mnemonic = "add"; - if (stack_op) { - mnemonic = "mov"; - form = form_mov; - } - break; - } - case ADDS_w_imm: - case ADDS_x_imm: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_imm: - case SUB_x_imm: mnemonic = "sub"; break; - case SUBS_w_imm: - case SUBS_x_imm: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubShifted(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'NDP"; - const char *form_cmp = "'Rn, 'Rm'NDP"; - const char *form_neg = "'Rd, 'Rm'NDP"; - - switch (instr->Mask(AddSubShiftedMask)) { - case ADD_w_shift: - case ADD_x_shift: mnemonic = "add"; break; - case ADDS_w_shift: - case ADDS_x_shift: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_shift: - case SUB_x_shift: { - mnemonic = "sub"; - if (rn_is_zr) { - mnemonic = "neg"; - form = form_neg; - } - break; - } - case SUBS_w_shift: - case SUBS_x_shift: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } else if (rn_is_zr) { - mnemonic = "negs"; - form = form_neg; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubExtended(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - const char *mnemonic = ""; - Extend mode = static_cast(instr->ExtendMode()); - const char *form = ((mode == UXTX) || (mode == SXTX)) ? - "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; - const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? - "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; - - switch (instr->Mask(AddSubExtendedMask)) { - case ADD_w_ext: - case ADD_x_ext: mnemonic = "add"; break; - case ADDS_w_ext: - case ADDS_x_ext: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_ext: - case SUB_x_ext: mnemonic = "sub"; break; - case SUBS_w_ext: - case SUBS_x_ext: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubWithCarry(const Instruction* instr) { - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm"; - const char *form_neg = "'Rd, 'Rm"; - - switch (instr->Mask(AddSubWithCarryMask)) { - case ADC_w: - case ADC_x: mnemonic = "adc"; break; - case ADCS_w: - case ADCS_x: mnemonic = "adcs"; break; - case SBC_w: - case SBC_x: { - mnemonic = "sbc"; - if (rn_is_zr) { - mnemonic = "ngc"; - form = form_neg; - } - break; - } - case SBCS_w: - case SBCS_x: { - mnemonic = "sbcs"; - if (rn_is_zr) { - mnemonic = "ngcs"; - form = form_neg; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLogicalImmediate(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rds, 'Rn, 'ITri"; - - if (instr->ImmLogical() == 0) { - // The immediate encoded in the instruction is not in the expected format. - Format(instr, "unallocated", "(LogicalImmediate)"); - return; - } - - switch (instr->Mask(LogicalImmediateMask)) { - case AND_w_imm: - case AND_x_imm: mnemonic = "and"; break; - case ORR_w_imm: - case ORR_x_imm: { - mnemonic = "orr"; - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize - : kWRegSize; - if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { - mnemonic = "mov"; - form = "'Rds, 'ITri"; - } - break; - } - case EOR_w_imm: - case EOR_x_imm: mnemonic = "eor"; break; - case ANDS_w_imm: - case ANDS_x_imm: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'ITri"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { - VIXL_ASSERT((reg_size == kXRegSize) || - ((reg_size == kWRegSize) && (value <= 0xffffffff))); - - // Test for movz: 16 bits set at positions 0, 16, 32 or 48. - if (((value & UINT64_C(0xffffffffffff0000)) == 0) || - ((value & UINT64_C(0xffffffff0000ffff)) == 0) || - ((value & UINT64_C(0xffff0000ffffffff)) == 0) || - ((value & UINT64_C(0x0000ffffffffffff)) == 0)) { - return true; - } - - // Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48). - if ((reg_size == kXRegSize) && - (((~value & UINT64_C(0xffffffffffff0000)) == 0) || - ((~value & UINT64_C(0xffffffff0000ffff)) == 0) || - ((~value & UINT64_C(0xffff0000ffffffff)) == 0) || - ((~value & UINT64_C(0x0000ffffffffffff)) == 0))) { - return true; - } - if ((reg_size == kWRegSize) && - (((value & 0xffff0000) == 0xffff0000) || - ((value & 0x0000ffff) == 0x0000ffff))) { - return true; - } - return false; -} - - -void Disassembler::VisitLogicalShifted(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'NLo"; - - switch (instr->Mask(LogicalShiftedMask)) { - case AND_w: - case AND_x: mnemonic = "and"; break; - case BIC_w: - case BIC_x: mnemonic = "bic"; break; - case EOR_w: - case EOR_x: mnemonic = "eor"; break; - case EON_w: - case EON_x: mnemonic = "eon"; break; - case BICS_w: - case BICS_x: mnemonic = "bics"; break; - case ANDS_w: - case ANDS_x: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'Rm'NLo"; - } - break; - } - case ORR_w: - case ORR_x: { - mnemonic = "orr"; - if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { - mnemonic = "mov"; - form = "'Rd, 'Rm"; - } - break; - } - case ORN_w: - case ORN_x: { - mnemonic = "orn"; - if (rn_is_zr) { - mnemonic = "mvn"; - form = "'Rd, 'Rm'NLo"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareRegister(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareRegisterMask)) { - case CCMN_w: - case CCMN_x: mnemonic = "ccmn"; break; - case CCMP_w: - case CCMP_x: mnemonic = "ccmp"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareImmediateMask)) { - case CCMN_w_imm: - case CCMN_x_imm: mnemonic = "ccmn"; break; - case CCMP_w_imm: - case CCMP_x_imm: mnemonic = "ccmp"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalSelect(const Instruction* instr) { - bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); - bool rn_is_rm = (instr->Rn() == instr->Rm()); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; - const char *form_test = "'Rd, 'CInv"; - const char *form_update = "'Rd, 'Rn, 'CInv"; - - Condition cond = static_cast(instr->Condition()); - bool invertible_cond = (cond != al) && (cond != nv); - - switch (instr->Mask(ConditionalSelectMask)) { - case CSEL_w: - case CSEL_x: mnemonic = "csel"; break; - case CSINC_w: - case CSINC_x: { - mnemonic = "csinc"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "cset"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinc"; - form = form_update; - } - break; - } - case CSINV_w: - case CSINV_x: { - mnemonic = "csinv"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "csetm"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinv"; - form = form_update; - } - break; - } - case CSNEG_w: - case CSNEG_x: { - mnemonic = "csneg"; - if (rn_is_rm && invertible_cond) { - mnemonic = "cneg"; - form = form_update; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitBitfield(const Instruction* instr) { - unsigned s = instr->ImmS(); - unsigned r = instr->ImmR(); - unsigned rd_size_minus_1 = - ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; - const char *mnemonic = ""; - const char *form = ""; - const char *form_shift_right = "'Rd, 'Rn, 'IBr"; - const char *form_extend = "'Rd, 'Wn"; - const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; - const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; - const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; - - switch (instr->Mask(BitfieldMask)) { - case SBFM_w: - case SBFM_x: { - mnemonic = "sbfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "sxtb"; - } else if (s == 15) { - mnemonic = "sxth"; - } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { - mnemonic = "sxtw"; - } else { - form = form_bfx; - } - } else if (s == rd_size_minus_1) { - mnemonic = "asr"; - form = form_shift_right; - } else if (s < r) { - mnemonic = "sbfiz"; - form = form_bfiz; - } - break; - } - case UBFM_w: - case UBFM_x: { - mnemonic = "ubfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "uxtb"; - } else if (s == 15) { - mnemonic = "uxth"; - } else { - form = form_bfx; - } - } - if (s == rd_size_minus_1) { - mnemonic = "lsr"; - form = form_shift_right; - } else if (r == s + 1) { - mnemonic = "lsl"; - form = form_lsl; - } else if (s < r) { - mnemonic = "ubfiz"; - form = form_bfiz; - } - break; - } - case BFM_w: - case BFM_x: { - mnemonic = "bfxil"; - form = form_bfx; - if (s < r) { - mnemonic = "bfi"; - form = form_bfiz; - } - } - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitExtract(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; - - switch (instr->Mask(ExtractMask)) { - case EXTR_w: - case EXTR_x: { - if (instr->Rn() == instr->Rm()) { - mnemonic = "ror"; - form = "'Rd, 'Rn, 'IExtract"; - } else { - mnemonic = "extr"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitPCRelAddressing(const Instruction* instr) { - switch (instr->Mask(PCRelAddressingMask)) { - case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; - case ADRP: Format(instr, "adrp", "'Xd, 'AddrPCRelPage"); break; - default: Format(instr, "unimplemented", "(PCRelAddressing)"); - } -} - - -void Disassembler::VisitConditionalBranch(const Instruction* instr) { - switch (instr->Mask(ConditionalBranchMask)) { - case B_cond: Format(instr, "b.'CBrn", "'TImmCond"); break; - default: VIXL_UNREACHABLE(); - } -} - - -void Disassembler::VisitUnconditionalBranchToRegister( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Xn"; - - switch (instr->Mask(UnconditionalBranchToRegisterMask)) { - case BR: mnemonic = "br"; break; - case BLR: mnemonic = "blr"; break; - case RET: { - mnemonic = "ret"; - if (instr->Rn() == kLinkRegCode) { - form = NULL; - } - break; - } - default: form = "(UnconditionalBranchToRegister)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitUnconditionalBranch(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'TImmUncn"; - - switch (instr->Mask(UnconditionalBranchMask)) { - case B: mnemonic = "b"; break; - case BL: mnemonic = "bl"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing1Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn"; - - switch (instr->Mask(DataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(RBIT, "rbit"); - FORMAT(REV16, "rev16"); - FORMAT(REV, "rev"); - FORMAT(CLZ, "clz"); - FORMAT(CLS, "cls"); - #undef FORMAT - case REV32_x: mnemonic = "rev32"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing2Source(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Rd, 'Rn, 'Rm"; - const char *form_wwx = "'Wd, 'Wn, 'Xm"; - - switch (instr->Mask(DataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(UDIV, "udiv"); - FORMAT(SDIV, "sdiv"); - FORMAT(LSLV, "lsl"); - FORMAT(LSRV, "lsr"); - FORMAT(ASRV, "asr"); - FORMAT(RORV, "ror"); - #undef FORMAT - case CRC32B: mnemonic = "crc32b"; break; - case CRC32H: mnemonic = "crc32h"; break; - case CRC32W: mnemonic = "crc32w"; break; - case CRC32X: mnemonic = "crc32x"; form = form_wwx; break; - case CRC32CB: mnemonic = "crc32cb"; break; - case CRC32CH: mnemonic = "crc32ch"; break; - case CRC32CW: mnemonic = "crc32cw"; break; - case CRC32CX: mnemonic = "crc32cx"; form = form_wwx; break; - default: form = "(DataProcessing2Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing3Source(const Instruction* instr) { - bool ra_is_zr = RaIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; - const char *form_rrr = "'Rd, 'Rn, 'Rm"; - const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; - const char *form_xww = "'Xd, 'Wn, 'Wm"; - const char *form_xxx = "'Xd, 'Xn, 'Xm"; - - switch (instr->Mask(DataProcessing3SourceMask)) { - case MADD_w: - case MADD_x: { - mnemonic = "madd"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mul"; - form = form_rrr; - } - break; - } - case MSUB_w: - case MSUB_x: { - mnemonic = "msub"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mneg"; - form = form_rrr; - } - break; - } - case SMADDL_x: { - mnemonic = "smaddl"; - if (ra_is_zr) { - mnemonic = "smull"; - form = form_xww; - } - break; - } - case SMSUBL_x: { - mnemonic = "smsubl"; - if (ra_is_zr) { - mnemonic = "smnegl"; - form = form_xww; - } - break; - } - case UMADDL_x: { - mnemonic = "umaddl"; - if (ra_is_zr) { - mnemonic = "umull"; - form = form_xww; - } - break; - } - case UMSUBL_x: { - mnemonic = "umsubl"; - if (ra_is_zr) { - mnemonic = "umnegl"; - form = form_xww; - } - break; - } - case SMULH_x: { - mnemonic = "smulh"; - form = form_xxx; - break; - } - case UMULH_x: { - mnemonic = "umulh"; - form = form_xxx; - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitCompareBranch(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rt, 'TImmCmpa"; - - switch (instr->Mask(CompareBranchMask)) { - case CBZ_w: - case CBZ_x: mnemonic = "cbz"; break; - case CBNZ_w: - case CBNZ_x: mnemonic = "cbnz"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitTestBranch(const Instruction* instr) { - const char *mnemonic = ""; - // If the top bit of the immediate is clear, the tested register is - // disassembled as Wt, otherwise Xt. As the top bit of the immediate is - // encoded in bit 31 of the instruction, we can reuse the Rt form, which - // uses bit 31 (normally "sf") to choose the register size. - const char *form = "'Rt, 'IS, 'TImmTest"; - - switch (instr->Mask(TestBranchMask)) { - case TBZ: mnemonic = "tbz"; break; - case TBNZ: mnemonic = "tbnz"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitMoveWideImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'IMoveImm"; - - // Print the shift separately for movk, to make it clear which half word will - // be overwritten. Movn and movz print the computed immediate, which includes - // shift calculation. - switch (instr->Mask(MoveWideImmediateMask)) { - case MOVN_w: - case MOVN_x: - if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) { - if ((instr->SixtyFourBits() == 0) && (instr->ImmMoveWide() == 0xffff)) { - mnemonic = "movn"; - } else { - mnemonic = "mov"; - form = "'Rd, 'IMoveNeg"; - } - } else { - mnemonic = "movn"; - } - break; - case MOVZ_w: - case MOVZ_x: - if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) - mnemonic = "mov"; - else - mnemonic = "movz"; - break; - case MOVK_w: - case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_LIST(V) \ - V(STRB_w, "strb", "'Wt") \ - V(STRH_w, "strh", "'Wt") \ - V(STR_w, "str", "'Wt") \ - V(STR_x, "str", "'Xt") \ - V(LDRB_w, "ldrb", "'Wt") \ - V(LDRH_w, "ldrh", "'Wt") \ - V(LDR_w, "ldr", "'Wt") \ - V(LDR_x, "ldr", "'Xt") \ - V(LDRSB_x, "ldrsb", "'Xt") \ - V(LDRSH_x, "ldrsh", "'Xt") \ - V(LDRSW_x, "ldrsw", "'Xt") \ - V(LDRSB_w, "ldrsb", "'Wt") \ - V(LDRSH_w, "ldrsh", "'Wt") \ - V(STR_b, "str", "'Bt") \ - V(STR_h, "str", "'Ht") \ - V(STR_s, "str", "'St") \ - V(STR_d, "str", "'Dt") \ - V(LDR_b, "ldr", "'Bt") \ - V(LDR_h, "ldr", "'Ht") \ - V(LDR_s, "ldr", "'St") \ - V(LDR_d, "ldr", "'Dt") \ - V(STR_q, "str", "'Qt") \ - V(LDR_q, "ldr", "'Qt") - -void Disassembler::VisitLoadStorePreIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePreIndex)"; - - switch (instr->Mask(LoadStorePreIndexMask)) { - #define LS_PREINDEX(A, B, C) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; - LOAD_STORE_LIST(LS_PREINDEX) - #undef LS_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePostIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePostIndex)"; - - switch (instr->Mask(LoadStorePostIndexMask)) { - #define LS_POSTINDEX(A, B, C) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; - LOAD_STORE_LIST(LS_POSTINDEX) - #undef LS_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreUnsignedOffset)"; - - switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { - #define LS_UNSIGNEDOFFSET(A, B, C) \ - case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; - LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) - #undef LS_UNSIGNEDOFFSET - case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xns'ILU]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreRegisterOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreRegisterOffset)"; - - switch (instr->Mask(LoadStoreRegisterOffsetMask)) { - #define LS_REGISTEROFFSET(A, B, C) \ - case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; - LOAD_STORE_LIST(LS_REGISTEROFFSET) - #undef LS_REGISTEROFFSET - case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Wt, ['Xns'ILS]"; - const char *form_x = "'Xt, ['Xns'ILS]"; - const char *form_b = "'Bt, ['Xns'ILS]"; - const char *form_h = "'Ht, ['Xns'ILS]"; - const char *form_s = "'St, ['Xns'ILS]"; - const char *form_d = "'Dt, ['Xns'ILS]"; - const char *form_q = "'Qt, ['Xns'ILS]"; - const char *form_prefetch = "'PrefOp, ['Xns'ILS]"; - - switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { - case STURB_w: mnemonic = "sturb"; break; - case STURH_w: mnemonic = "sturh"; break; - case STUR_w: mnemonic = "stur"; break; - case STUR_x: mnemonic = "stur"; form = form_x; break; - case STUR_b: mnemonic = "stur"; form = form_b; break; - case STUR_h: mnemonic = "stur"; form = form_h; break; - case STUR_s: mnemonic = "stur"; form = form_s; break; - case STUR_d: mnemonic = "stur"; form = form_d; break; - case STUR_q: mnemonic = "stur"; form = form_q; break; - case LDURB_w: mnemonic = "ldurb"; break; - case LDURH_w: mnemonic = "ldurh"; break; - case LDUR_w: mnemonic = "ldur"; break; - case LDUR_x: mnemonic = "ldur"; form = form_x; break; - case LDUR_b: mnemonic = "ldur"; form = form_b; break; - case LDUR_h: mnemonic = "ldur"; form = form_h; break; - case LDUR_s: mnemonic = "ldur"; form = form_s; break; - case LDUR_d: mnemonic = "ldur"; form = form_d; break; - case LDUR_q: mnemonic = "ldur"; form = form_q; break; - case LDURSB_x: form = form_x; VIXL_FALLTHROUGH(); - case LDURSB_w: mnemonic = "ldursb"; break; - case LDURSH_x: form = form_x; VIXL_FALLTHROUGH(); - case LDURSH_w: mnemonic = "ldursh"; break; - case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; - case PRFUM: mnemonic = "prfum"; form = form_prefetch; break; - default: form = "(LoadStoreUnscaledOffset)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadLiteral(const Instruction* instr) { - const char *mnemonic = "ldr"; - const char *form = "(LoadLiteral)"; - - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; - case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; - case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; - case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; - case LDR_q_lit: form = "'Qt, 'ILLiteral 'LValue"; break; - case LDRSW_x_lit: { - mnemonic = "ldrsw"; - form = "'Xt, 'ILLiteral 'LValue"; - break; - } - case PRFM_lit: { - mnemonic = "prfm"; - form = "'PrefOp, 'ILLiteral 'LValue"; - break; - } - default: mnemonic = "unimplemented"; - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_PAIR_LIST(V) \ - V(STP_w, "stp", "'Wt, 'Wt2", "2") \ - V(LDP_w, "ldp", "'Wt, 'Wt2", "2") \ - V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "2") \ - V(STP_x, "stp", "'Xt, 'Xt2", "3") \ - V(LDP_x, "ldp", "'Xt, 'Xt2", "3") \ - V(STP_s, "stp", "'St, 'St2", "2") \ - V(LDP_s, "ldp", "'St, 'St2", "2") \ - V(STP_d, "stp", "'Dt, 'Dt2", "3") \ - V(LDP_d, "ldp", "'Dt, 'Dt2", "3") \ - V(LDP_q, "ldp", "'Qt, 'Qt2", "4") \ - V(STP_q, "stp", "'Qt, 'Qt2", "4") - -void Disassembler::VisitLoadStorePairPostIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPostIndex)"; - - switch (instr->Mask(LoadStorePairPostIndexMask)) { - #define LSP_POSTINDEX(A, B, C, D) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; - LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) - #undef LSP_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairPreIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPreIndex)"; - - switch (instr->Mask(LoadStorePairPreIndexMask)) { - #define LSP_PREINDEX(A, B, C, D) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; - LOAD_STORE_PAIR_LIST(LSP_PREINDEX) - #undef LSP_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairOffset)"; - - switch (instr->Mask(LoadStorePairOffsetMask)) { - #define LSP_OFFSET(A, B, C, D) \ - case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; - LOAD_STORE_PAIR_LIST(LSP_OFFSET) - #undef LSP_OFFSET - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairNonTemporal(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStorePairNonTemporalMask)) { - case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP2]"; break; - case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP2]"; break; - case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP3]"; break; - case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP3]"; break; - case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP2]"; break; - case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP2]"; break; - case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP3]"; break; - case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP3]"; break; - case STNP_q: mnemonic = "stnp"; form = "'Qt, 'Qt2, ['Xns'ILP4]"; break; - case LDNP_q: mnemonic = "ldnp"; form = "'Qt, 'Qt2, ['Xns'ILP4]"; break; - default: form = "(LoadStorePairNonTemporal)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreExclusive(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStoreExclusiveMask)) { - case STXRB_w: mnemonic = "stxrb"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXRH_w: mnemonic = "stxrh"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXR_w: mnemonic = "stxr"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXR_x: mnemonic = "stxr"; form = "'Ws, 'Xt, ['Xns]"; break; - case LDXRB_w: mnemonic = "ldxrb"; form = "'Wt, ['Xns]"; break; - case LDXRH_w: mnemonic = "ldxrh"; form = "'Wt, ['Xns]"; break; - case LDXR_w: mnemonic = "ldxr"; form = "'Wt, ['Xns]"; break; - case LDXR_x: mnemonic = "ldxr"; form = "'Xt, ['Xns]"; break; - case STXP_w: mnemonic = "stxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; - case STXP_x: mnemonic = "stxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; - case LDXP_w: mnemonic = "ldxp"; form = "'Wt, 'Wt2, ['Xns]"; break; - case LDXP_x: mnemonic = "ldxp"; form = "'Xt, 'Xt2, ['Xns]"; break; - case STLXRB_w: mnemonic = "stlxrb"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXRH_w: mnemonic = "stlxrh"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXR_w: mnemonic = "stlxr"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXR_x: mnemonic = "stlxr"; form = "'Ws, 'Xt, ['Xns]"; break; - case LDAXRB_w: mnemonic = "ldaxrb"; form = "'Wt, ['Xns]"; break; - case LDAXRH_w: mnemonic = "ldaxrh"; form = "'Wt, ['Xns]"; break; - case LDAXR_w: mnemonic = "ldaxr"; form = "'Wt, ['Xns]"; break; - case LDAXR_x: mnemonic = "ldaxr"; form = "'Xt, ['Xns]"; break; - case STLXP_w: mnemonic = "stlxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; - case STLXP_x: mnemonic = "stlxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; - case LDAXP_w: mnemonic = "ldaxp"; form = "'Wt, 'Wt2, ['Xns]"; break; - case LDAXP_x: mnemonic = "ldaxp"; form = "'Xt, 'Xt2, ['Xns]"; break; - case STLRB_w: mnemonic = "stlrb"; form = "'Wt, ['Xns]"; break; - case STLRH_w: mnemonic = "stlrh"; form = "'Wt, ['Xns]"; break; - case STLR_w: mnemonic = "stlr"; form = "'Wt, ['Xns]"; break; - case STLR_x: mnemonic = "stlr"; form = "'Xt, ['Xns]"; break; - case LDARB_w: mnemonic = "ldarb"; form = "'Wt, ['Xns]"; break; - case LDARH_w: mnemonic = "ldarh"; form = "'Wt, ['Xns]"; break; - case LDAR_w: mnemonic = "ldar"; form = "'Wt, ['Xns]"; break; - case LDAR_x: mnemonic = "ldar"; form = "'Xt, ['Xns]"; break; - default: form = "(LoadStoreExclusive)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPCompare(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fn, 'Fm"; - const char *form_zero = "'Fn, #0.0"; - - switch (instr->Mask(FPCompareMask)) { - case FCMP_s_zero: - case FCMP_d_zero: form = form_zero; VIXL_FALLTHROUGH(); - case FCMP_s: - case FCMP_d: mnemonic = "fcmp"; break; - case FCMPE_s_zero: - case FCMPE_d_zero: form = form_zero; VIXL_FALLTHROUGH(); - case FCMPE_s: - case FCMPE_d: mnemonic = "fcmpe"; break; - default: form = "(FPCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalCompare(const Instruction* instr) { - const char *mnemonic = "unmplemented"; - const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; - - switch (instr->Mask(FPConditionalCompareMask)) { - case FCCMP_s: - case FCCMP_d: mnemonic = "fccmp"; break; - case FCCMPE_s: - case FCCMPE_d: mnemonic = "fccmpe"; break; - default: form = "(FPConditionalCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalSelect(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; - - switch (instr->Mask(FPConditionalSelectMask)) { - case FCSEL_s: - case FCSEL_d: mnemonic = "fcsel"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing1Source(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fd, 'Fn"; - - switch (instr->Mask(FPDataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMOV, "fmov"); - FORMAT(FABS, "fabs"); - FORMAT(FNEG, "fneg"); - FORMAT(FSQRT, "fsqrt"); - FORMAT(FRINTN, "frintn"); - FORMAT(FRINTP, "frintp"); - FORMAT(FRINTM, "frintm"); - FORMAT(FRINTZ, "frintz"); - FORMAT(FRINTA, "frinta"); - FORMAT(FRINTX, "frintx"); - FORMAT(FRINTI, "frinti"); - #undef FORMAT - case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; - case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; - case FCVT_hs: mnemonic = "fcvt"; form = "'Hd, 'Sn"; break; - case FCVT_sh: mnemonic = "fcvt"; form = "'Sd, 'Hn"; break; - case FCVT_dh: mnemonic = "fcvt"; form = "'Dd, 'Hn"; break; - case FCVT_hd: mnemonic = "fcvt"; form = "'Hd, 'Dn"; break; - default: form = "(FPDataProcessing1Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing2Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm"; - - switch (instr->Mask(FPDataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMUL, "fmul"); - FORMAT(FDIV, "fdiv"); - FORMAT(FADD, "fadd"); - FORMAT(FSUB, "fsub"); - FORMAT(FMAX, "fmax"); - FORMAT(FMIN, "fmin"); - FORMAT(FMAXNM, "fmaxnm"); - FORMAT(FMINNM, "fminnm"); - FORMAT(FNMUL, "fnmul"); - #undef FORMAT - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing3Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; - - switch (instr->Mask(FPDataProcessing3SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMADD, "fmadd"); - FORMAT(FMSUB, "fmsub"); - FORMAT(FNMADD, "fnmadd"); - FORMAT(FNMSUB, "fnmsub"); - #undef FORMAT - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "(FPImmediate)"; - - switch (instr->Mask(FPImmediateMask)) { - case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; - case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPIntegerConvert(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(FPIntegerConvert)"; - const char *form_rf = "'Rd, 'Fn"; - const char *form_fr = "'Fd, 'Rn"; - - switch (instr->Mask(FPIntegerConvertMask)) { - case FMOV_ws: - case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; - case FMOV_sw: - case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; - case FMOV_d1_x: mnemonic = "fmov"; form = "'Vd.D[1], 'Rn"; break; - case FMOV_x_d1: mnemonic = "fmov"; form = "'Rd, 'Vn.D[1]"; break; - case FCVTAS_ws: - case FCVTAS_xs: - case FCVTAS_wd: - case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; - case FCVTAU_ws: - case FCVTAU_xs: - case FCVTAU_wd: - case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; - case FCVTMS_ws: - case FCVTMS_xs: - case FCVTMS_wd: - case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; - case FCVTMU_ws: - case FCVTMU_xs: - case FCVTMU_wd: - case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; - case FCVTNS_ws: - case FCVTNS_xs: - case FCVTNS_wd: - case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; - case FCVTNU_ws: - case FCVTNU_xs: - case FCVTNU_wd: - case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; - case FCVTZU_xd: - case FCVTZU_ws: - case FCVTZU_wd: - case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; - case FCVTZS_xd: - case FCVTZS_wd: - case FCVTZS_xs: - case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; - case FCVTPU_xd: - case FCVTPU_ws: - case FCVTPU_wd: - case FCVTPU_xs: mnemonic = "fcvtpu"; form = form_rf; break; - case FCVTPS_xd: - case FCVTPS_wd: - case FCVTPS_xs: - case FCVTPS_ws: mnemonic = "fcvtps"; form = form_rf; break; - case SCVTF_sw: - case SCVTF_sx: - case SCVTF_dw: - case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw: - case UCVTF_sx: - case UCVTF_dw: - case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPFixedPointConvert(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Fn, 'IFPFBits"; - const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; - - switch (instr->Mask(FPFixedPointConvertMask)) { - case FCVTZS_ws_fixed: - case FCVTZS_xs_fixed: - case FCVTZS_wd_fixed: - case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; - case FCVTZU_ws_fixed: - case FCVTZU_xs_fixed: - case FCVTZU_wd_fixed: - case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; - case SCVTF_sw_fixed: - case SCVTF_sx_fixed: - case SCVTF_dw_fixed: - case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw_fixed: - case UCVTF_sx_fixed: - case UCVTF_dw_fixed: - case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitSystem(const Instruction* instr) { - // Some system instructions hijack their Op and Cp fields to represent a - // range of immediates instead of indicating a different instruction. This - // makes the decoding tricky. - const char *mnemonic = "unimplemented"; - const char *form = "(System)"; - - if (instr->Mask(SystemExclusiveMonitorFMask) == SystemExclusiveMonitorFixed) { - switch (instr->Mask(SystemExclusiveMonitorMask)) { - case CLREX: { - mnemonic = "clrex"; - form = (instr->CRm() == 0xf) ? NULL : "'IX"; - break; - } - } - } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { - switch (instr->Mask(SystemSysRegMask)) { - case MRS: { - mnemonic = "mrs"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "'Xt, nzcv"; break; - case FPCR: form = "'Xt, fpcr"; break; - default: form = "'Xt, (unknown)"; break; - } - break; - } - case MSR: { - mnemonic = "msr"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "nzcv, 'Xt"; break; - case FPCR: form = "fpcr, 'Xt"; break; - default: form = "(unknown), 'Xt"; break; - } - break; - } - } - } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { - switch (instr->ImmHint()) { - case NOP: { - mnemonic = "nop"; - form = NULL; - break; - } - } - } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { - switch (instr->Mask(MemBarrierMask)) { - case DMB: { - mnemonic = "dmb"; - form = "'M"; - break; - } - case DSB: { - mnemonic = "dsb"; - form = "'M"; - break; - } - case ISB: { - mnemonic = "isb"; - form = NULL; - break; - } - } - } else if (instr->Mask(SystemSysFMask) == SystemSysFixed) { - switch (instr->SysOp()) { - case IVAU: - mnemonic = "ic"; - form = "ivau, 'Xt"; - break; - case CVAC: - mnemonic = "dc"; - form = "cvac, 'Xt"; - break; - case CVAU: - mnemonic = "dc"; - form = "cvau, 'Xt"; - break; - case CIVAC: - mnemonic = "dc"; - form = "civac, 'Xt"; - break; - case ZVA: - mnemonic = "dc"; - form = "zva, 'Xt"; - break; - default: - mnemonic = "sys"; - if (instr->Rt() == 31) { - form = "'G1, 'Kn, 'Km, 'G2"; - } else { - form = "'G1, 'Kn, 'Km, 'G2, 'Xt"; - } - break; - } - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitException(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'IDebug"; - - switch (instr->Mask(ExceptionMask)) { - case HLT: mnemonic = "hlt"; break; - case BRK: mnemonic = "brk"; break; - case SVC: mnemonic = "svc"; break; - case HVC: mnemonic = "hvc"; break; - case SMC: mnemonic = "smc"; break; - case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; - case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; - case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; - default: form = "(Exception)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitCrypto2RegSHA(const Instruction* instr) { - VisitUnimplemented(instr); -} - - -void Disassembler::VisitCrypto3RegSHA(const Instruction* instr) { - VisitUnimplemented(instr); -} - - -void Disassembler::VisitCryptoAES(const Instruction* instr) { - VisitUnimplemented(instr); -} - - -void Disassembler::VisitNEON2RegMisc(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s"; - const char *form_cmp_zero = "'Vd.%s, 'Vn.%s, #0"; - const char *form_fcmp_zero = "'Vd.%s, 'Vn.%s, #0.0"; - NEONFormatDecoder nfd(instr); - - static const NEONFormatMap map_lp_ta = { - {23, 22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D} - }; - - static const NEONFormatMap map_cvt_ta = { - {22}, {NF_4S, NF_2D} - }; - - static const NEONFormatMap map_cvt_tb = { - {22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S} - }; - - if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_opcode) { - // These instructions all use a two bit size field, except NOT and RBIT, - // which use the field to encode the operation. - switch (instr->Mask(NEON2RegMiscMask)) { - case NEON_REV64: mnemonic = "rev64"; break; - case NEON_REV32: mnemonic = "rev32"; break; - case NEON_REV16: mnemonic = "rev16"; break; - case NEON_SADDLP: - mnemonic = "saddlp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_UADDLP: - mnemonic = "uaddlp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_SUQADD: mnemonic = "suqadd"; break; - case NEON_USQADD: mnemonic = "usqadd"; break; - case NEON_CLS: mnemonic = "cls"; break; - case NEON_CLZ: mnemonic = "clz"; break; - case NEON_CNT: mnemonic = "cnt"; break; - case NEON_SADALP: - mnemonic = "sadalp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_UADALP: - mnemonic = "uadalp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_SQABS: mnemonic = "sqabs"; break; - case NEON_SQNEG: mnemonic = "sqneg"; break; - case NEON_CMGT_zero: mnemonic = "cmgt"; form = form_cmp_zero; break; - case NEON_CMGE_zero: mnemonic = "cmge"; form = form_cmp_zero; break; - case NEON_CMEQ_zero: mnemonic = "cmeq"; form = form_cmp_zero; break; - case NEON_CMLE_zero: mnemonic = "cmle"; form = form_cmp_zero; break; - case NEON_CMLT_zero: mnemonic = "cmlt"; form = form_cmp_zero; break; - case NEON_ABS: mnemonic = "abs"; break; - case NEON_NEG: mnemonic = "neg"; break; - case NEON_RBIT_NOT: - switch (instr->FPType()) { - case 0: mnemonic = "mvn"; break; - case 1: mnemonic = "rbit"; break; - default: form = "(NEON2RegMisc)"; - } - nfd.SetFormatMaps(nfd.LogicalFormatMap()); - break; - } - } else { - // These instructions all use a one bit size field, except XTN, SQXTUN, - // SHLL, SQXTN and UQXTN, which use a two bit size field. - nfd.SetFormatMaps(nfd.FPFormatMap()); - switch (instr->Mask(NEON2RegMiscFPMask)) { - case NEON_FABS: mnemonic = "fabs"; break; - case NEON_FNEG: mnemonic = "fneg"; break; - case NEON_FCVTN: - mnemonic = instr->Mask(NEON_Q) ? "fcvtn2" : "fcvtn"; - nfd.SetFormatMap(0, &map_cvt_tb); - nfd.SetFormatMap(1, &map_cvt_ta); - break; - case NEON_FCVTXN: - mnemonic = instr->Mask(NEON_Q) ? "fcvtxn2" : "fcvtxn"; - nfd.SetFormatMap(0, &map_cvt_tb); - nfd.SetFormatMap(1, &map_cvt_ta); - break; - case NEON_FCVTL: - mnemonic = instr->Mask(NEON_Q) ? "fcvtl2" : "fcvtl"; - nfd.SetFormatMap(0, &map_cvt_ta); - nfd.SetFormatMap(1, &map_cvt_tb); - break; - case NEON_FRINTN: mnemonic = "frintn"; break; - case NEON_FRINTA: mnemonic = "frinta"; break; - case NEON_FRINTP: mnemonic = "frintp"; break; - case NEON_FRINTM: mnemonic = "frintm"; break; - case NEON_FRINTX: mnemonic = "frintx"; break; - case NEON_FRINTZ: mnemonic = "frintz"; break; - case NEON_FRINTI: mnemonic = "frinti"; break; - case NEON_FCVTNS: mnemonic = "fcvtns"; break; - case NEON_FCVTNU: mnemonic = "fcvtnu"; break; - case NEON_FCVTPS: mnemonic = "fcvtps"; break; - case NEON_FCVTPU: mnemonic = "fcvtpu"; break; - case NEON_FCVTMS: mnemonic = "fcvtms"; break; - case NEON_FCVTMU: mnemonic = "fcvtmu"; break; - case NEON_FCVTZS: mnemonic = "fcvtzs"; break; - case NEON_FCVTZU: mnemonic = "fcvtzu"; break; - case NEON_FCVTAS: mnemonic = "fcvtas"; break; - case NEON_FCVTAU: mnemonic = "fcvtau"; break; - case NEON_FSQRT: mnemonic = "fsqrt"; break; - case NEON_SCVTF: mnemonic = "scvtf"; break; - case NEON_UCVTF: mnemonic = "ucvtf"; break; - case NEON_URSQRTE: mnemonic = "ursqrte"; break; - case NEON_URECPE: mnemonic = "urecpe"; break; - case NEON_FRSQRTE: mnemonic = "frsqrte"; break; - case NEON_FRECPE: mnemonic = "frecpe"; break; - case NEON_FCMGT_zero: mnemonic = "fcmgt"; form = form_fcmp_zero; break; - case NEON_FCMGE_zero: mnemonic = "fcmge"; form = form_fcmp_zero; break; - case NEON_FCMEQ_zero: mnemonic = "fcmeq"; form = form_fcmp_zero; break; - case NEON_FCMLE_zero: mnemonic = "fcmle"; form = form_fcmp_zero; break; - case NEON_FCMLT_zero: mnemonic = "fcmlt"; form = form_fcmp_zero; break; - default: - if ((NEON_XTN_opcode <= instr->Mask(NEON2RegMiscOpcode)) && - (instr->Mask(NEON2RegMiscOpcode) <= NEON_UQXTN_opcode)) { - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - - switch (instr->Mask(NEON2RegMiscMask)) { - case NEON_XTN: mnemonic = "xtn"; break; - case NEON_SQXTN: mnemonic = "sqxtn"; break; - case NEON_UQXTN: mnemonic = "uqxtn"; break; - case NEON_SQXTUN: mnemonic = "sqxtun"; break; - case NEON_SHLL: - mnemonic = "shll"; - nfd.SetFormatMap(0, nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(1, nfd.IntegerFormatMap()); - switch (instr->NEONSize()) { - case 0: form = "'Vd.%s, 'Vn.%s, #8"; break; - case 1: form = "'Vd.%s, 'Vn.%s, #16"; break; - case 2: form = "'Vd.%s, 'Vn.%s, #32"; break; - default: form = "(NEON2RegMisc)"; - } - } - Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); - return; - } else { - form = "(NEON2RegMisc)"; - } - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEON3Same(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; - NEONFormatDecoder nfd(instr); - - if (instr->Mask(NEON3SameLogicalFMask) == NEON3SameLogicalFixed) { - switch (instr->Mask(NEON3SameLogicalMask)) { - case NEON_AND: mnemonic = "and"; break; - case NEON_ORR: - mnemonic = "orr"; - if (instr->Rm() == instr->Rn()) { - mnemonic = "mov"; - form = "'Vd.%s, 'Vn.%s"; - } - break; - case NEON_ORN: mnemonic = "orn"; break; - case NEON_EOR: mnemonic = "eor"; break; - case NEON_BIC: mnemonic = "bic"; break; - case NEON_BIF: mnemonic = "bif"; break; - case NEON_BIT: mnemonic = "bit"; break; - case NEON_BSL: mnemonic = "bsl"; break; - default: form = "(NEON3Same)"; - } - nfd.SetFormatMaps(nfd.LogicalFormatMap()); - } else { - static const char *mnemonics[] = { - "shadd", "uhadd", "shadd", "uhadd", - "sqadd", "uqadd", "sqadd", "uqadd", - "srhadd", "urhadd", "srhadd", "urhadd", - NULL, NULL, NULL, NULL, // Handled by logical cases above. - "shsub", "uhsub", "shsub", "uhsub", - "sqsub", "uqsub", "sqsub", "uqsub", - "cmgt", "cmhi", "cmgt", "cmhi", - "cmge", "cmhs", "cmge", "cmhs", - "sshl", "ushl", "sshl", "ushl", - "sqshl", "uqshl", "sqshl", "uqshl", - "srshl", "urshl", "srshl", "urshl", - "sqrshl", "uqrshl", "sqrshl", "uqrshl", - "smax", "umax", "smax", "umax", - "smin", "umin", "smin", "umin", - "sabd", "uabd", "sabd", "uabd", - "saba", "uaba", "saba", "uaba", - "add", "sub", "add", "sub", - "cmtst", "cmeq", "cmtst", "cmeq", - "mla", "mls", "mla", "mls", - "mul", "pmul", "mul", "pmul", - "smaxp", "umaxp", "smaxp", "umaxp", - "sminp", "uminp", "sminp", "uminp", - "sqdmulh", "sqrdmulh", "sqdmulh", "sqrdmulh", - "addp", "unallocated", "addp", "unallocated", - "fmaxnm", "fmaxnmp", "fminnm", "fminnmp", - "fmla", "unallocated", "fmls", "unallocated", - "fadd", "faddp", "fsub", "fabd", - "fmulx", "fmul", "unallocated", "unallocated", - "fcmeq", "fcmge", "unallocated", "fcmgt", - "unallocated", "facge", "unallocated", "facgt", - "fmax", "fmaxp", "fmin", "fminp", - "frecps", "fdiv", "frsqrts", "unallocated"}; - - // Operation is determined by the opcode bits (15-11), the top bit of - // size (23) and the U bit (29). - unsigned index = (instr->Bits(15, 11) << 2) | (instr->Bit(23) << 1) | - instr->Bit(29); - VIXL_ASSERT(index < (sizeof(mnemonics) / sizeof(mnemonics[0]))); - mnemonic = mnemonics[index]; - // Assert that index is not one of the previously handled logical - // instructions. - VIXL_ASSERT(mnemonic != NULL); - - if (instr->Mask(NEON3SameFPFMask) == NEON3SameFPFixed) { - nfd.SetFormatMaps(nfd.FPFormatMap()); - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEON3Different(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; - - NEONFormatDecoder nfd(instr); - nfd.SetFormatMap(0, nfd.LongIntegerFormatMap()); - - // Ignore the Q bit. Appending a "2" suffix is handled later. - switch (instr->Mask(NEON3DifferentMask) & ~NEON_Q) { - case NEON_PMULL: mnemonic = "pmull"; break; - case NEON_SABAL: mnemonic = "sabal"; break; - case NEON_SABDL: mnemonic = "sabdl"; break; - case NEON_SADDL: mnemonic = "saddl"; break; - case NEON_SMLAL: mnemonic = "smlal"; break; - case NEON_SMLSL: mnemonic = "smlsl"; break; - case NEON_SMULL: mnemonic = "smull"; break; - case NEON_SSUBL: mnemonic = "ssubl"; break; - case NEON_SQDMLAL: mnemonic = "sqdmlal"; break; - case NEON_SQDMLSL: mnemonic = "sqdmlsl"; break; - case NEON_SQDMULL: mnemonic = "sqdmull"; break; - case NEON_UABAL: mnemonic = "uabal"; break; - case NEON_UABDL: mnemonic = "uabdl"; break; - case NEON_UADDL: mnemonic = "uaddl"; break; - case NEON_UMLAL: mnemonic = "umlal"; break; - case NEON_UMLSL: mnemonic = "umlsl"; break; - case NEON_UMULL: mnemonic = "umull"; break; - case NEON_USUBL: mnemonic = "usubl"; break; - case NEON_SADDW: - mnemonic = "saddw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_SSUBW: - mnemonic = "ssubw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_UADDW: - mnemonic = "uaddw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_USUBW: - mnemonic = "usubw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_ADDHN: - mnemonic = "addhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - case NEON_RADDHN: - mnemonic = "raddhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - case NEON_RSUBHN: - mnemonic = "rsubhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - case NEON_SUBHN: - mnemonic = "subhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - default: form = "(NEON3Different)"; - } - Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONAcrossLanes(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, 'Vn.%s"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap(), - NEONFormatDecoder::IntegerFormatMap()); - - if (instr->Mask(NEONAcrossLanesFPFMask) == NEONAcrossLanesFPFixed) { - nfd.SetFormatMap(0, nfd.FPScalarFormatMap()); - nfd.SetFormatMap(1, nfd.FPFormatMap()); - switch (instr->Mask(NEONAcrossLanesFPMask)) { - case NEON_FMAXV: mnemonic = "fmaxv"; break; - case NEON_FMINV: mnemonic = "fminv"; break; - case NEON_FMAXNMV: mnemonic = "fmaxnmv"; break; - case NEON_FMINNMV: mnemonic = "fminnmv"; break; - default: form = "(NEONAcrossLanes)"; break; - } - } else if (instr->Mask(NEONAcrossLanesFMask) == NEONAcrossLanesFixed) { - switch (instr->Mask(NEONAcrossLanesMask)) { - case NEON_ADDV: mnemonic = "addv"; break; - case NEON_SMAXV: mnemonic = "smaxv"; break; - case NEON_SMINV: mnemonic = "sminv"; break; - case NEON_UMAXV: mnemonic = "umaxv"; break; - case NEON_UMINV: mnemonic = "uminv"; break; - case NEON_SADDLV: - mnemonic = "saddlv"; - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - break; - case NEON_UADDLV: - mnemonic = "uaddlv"; - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - break; - default: form = "(NEONAcrossLanes)"; break; - } - } - Format(instr, mnemonic, nfd.Substitute(form, - NEONFormatDecoder::kPlaceholder, NEONFormatDecoder::kFormat)); -} - - -void Disassembler::VisitNEONByIndexedElement(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - bool l_instr = false; - bool fp_instr = false; - - const char *form = "'Vd.%s, 'Vn.%s, 'Ve.%s['IVByElemIndex]"; - - static const NEONFormatMap map_ta = { - {23, 22}, {NF_UNDEF, NF_4S, NF_2D} - }; - NEONFormatDecoder nfd(instr, &map_ta, - NEONFormatDecoder::IntegerFormatMap(), - NEONFormatDecoder::ScalarFormatMap()); - - switch (instr->Mask(NEONByIndexedElementMask)) { - case NEON_SMULL_byelement: mnemonic = "smull"; l_instr = true; break; - case NEON_UMULL_byelement: mnemonic = "umull"; l_instr = true; break; - case NEON_SMLAL_byelement: mnemonic = "smlal"; l_instr = true; break; - case NEON_UMLAL_byelement: mnemonic = "umlal"; l_instr = true; break; - case NEON_SMLSL_byelement: mnemonic = "smlsl"; l_instr = true; break; - case NEON_UMLSL_byelement: mnemonic = "umlsl"; l_instr = true; break; - case NEON_SQDMULL_byelement: mnemonic = "sqdmull"; l_instr = true; break; - case NEON_SQDMLAL_byelement: mnemonic = "sqdmlal"; l_instr = true; break; - case NEON_SQDMLSL_byelement: mnemonic = "sqdmlsl"; l_instr = true; break; - case NEON_MUL_byelement: mnemonic = "mul"; break; - case NEON_MLA_byelement: mnemonic = "mla"; break; - case NEON_MLS_byelement: mnemonic = "mls"; break; - case NEON_SQDMULH_byelement: mnemonic = "sqdmulh"; break; - case NEON_SQRDMULH_byelement: mnemonic = "sqrdmulh"; break; - default: - switch (instr->Mask(NEONByIndexedElementFPMask)) { - case NEON_FMUL_byelement: mnemonic = "fmul"; fp_instr = true; break; - case NEON_FMLA_byelement: mnemonic = "fmla"; fp_instr = true; break; - case NEON_FMLS_byelement: mnemonic = "fmls"; fp_instr = true; break; - case NEON_FMULX_byelement: mnemonic = "fmulx"; fp_instr = true; break; - } - } - - if (l_instr) { - Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); - } else if (fp_instr) { - nfd.SetFormatMap(0, nfd.FPFormatMap()); - Format(instr, mnemonic, nfd.Substitute(form)); - } else { - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - Format(instr, mnemonic, nfd.Substitute(form)); - } -} - - -void Disassembler::VisitNEONCopy(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONCopy)"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularFormatMap(), - NEONFormatDecoder::TriangularScalarFormatMap()); - - if (instr->Mask(NEONCopyInsElementMask) == NEON_INS_ELEMENT) { - mnemonic = "mov"; - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - form = "'Vd.%s['IVInsIndex1], 'Vn.%s['IVInsIndex2]"; - } else if (instr->Mask(NEONCopyInsGeneralMask) == NEON_INS_GENERAL) { - mnemonic = "mov"; - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - if (nfd.GetVectorFormat() == kFormatD) { - form = "'Vd.%s['IVInsIndex1], 'Xn"; - } else { - form = "'Vd.%s['IVInsIndex1], 'Wn"; - } - } else if (instr->Mask(NEONCopyUmovMask) == NEON_UMOV) { - if (instr->Mask(NEON_Q) || ((instr->ImmNEON5() & 7) == 4)) { - mnemonic = "mov"; - } else { - mnemonic = "umov"; - } - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - if (nfd.GetVectorFormat() == kFormatD) { - form = "'Xd, 'Vn.%s['IVInsIndex1]"; - } else { - form = "'Wd, 'Vn.%s['IVInsIndex1]"; - } - } else if (instr->Mask(NEONCopySmovMask) == NEON_SMOV) { - mnemonic = "smov"; - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - form = "'Rdq, 'Vn.%s['IVInsIndex1]"; - } else if (instr->Mask(NEONCopyDupElementMask) == NEON_DUP_ELEMENT) { - mnemonic = "dup"; - form = "'Vd.%s, 'Vn.%s['IVInsIndex1]"; - } else if (instr->Mask(NEONCopyDupGeneralMask) == NEON_DUP_GENERAL) { - mnemonic = "dup"; - if (nfd.GetVectorFormat() == kFormat2D) { - form = "'Vd.%s, 'Xn"; - } else { - form = "'Vd.%s, 'Wn"; - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONExtract(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONExtract)"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap()); - if (instr->Mask(NEONExtractMask) == NEON_EXT) { - mnemonic = "ext"; - form = "'Vd.%s, 'Vn.%s, 'Vm.%s, 'IVExtract"; - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreMultiStruct(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreMultiStruct)"; - const char *form_1v = "{'Vt.%1$s}, ['Xns]"; - const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns]"; - const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns]"; - const char *form_4v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreMultiStructMask)) { - case NEON_LD1_1v: mnemonic = "ld1"; form = form_1v; break; - case NEON_LD1_2v: mnemonic = "ld1"; form = form_2v; break; - case NEON_LD1_3v: mnemonic = "ld1"; form = form_3v; break; - case NEON_LD1_4v: mnemonic = "ld1"; form = form_4v; break; - case NEON_LD2: mnemonic = "ld2"; form = form_2v; break; - case NEON_LD3: mnemonic = "ld3"; form = form_3v; break; - case NEON_LD4: mnemonic = "ld4"; form = form_4v; break; - case NEON_ST1_1v: mnemonic = "st1"; form = form_1v; break; - case NEON_ST1_2v: mnemonic = "st1"; form = form_2v; break; - case NEON_ST1_3v: mnemonic = "st1"; form = form_3v; break; - case NEON_ST1_4v: mnemonic = "st1"; form = form_4v; break; - case NEON_ST2: mnemonic = "st2"; form = form_2v; break; - case NEON_ST3: mnemonic = "st3"; form = form_3v; break; - case NEON_ST4: mnemonic = "st4"; form = form_4v; break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreMultiStructPostIndex( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreMultiStructPostIndex)"; - const char *form_1v = "{'Vt.%1$s}, ['Xns], 'Xmr1"; - const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns], 'Xmr2"; - const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns], 'Xmr3"; - const char *form_4v = - "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmr4"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) { - case NEON_LD1_1v_post: mnemonic = "ld1"; form = form_1v; break; - case NEON_LD1_2v_post: mnemonic = "ld1"; form = form_2v; break; - case NEON_LD1_3v_post: mnemonic = "ld1"; form = form_3v; break; - case NEON_LD1_4v_post: mnemonic = "ld1"; form = form_4v; break; - case NEON_LD2_post: mnemonic = "ld2"; form = form_2v; break; - case NEON_LD3_post: mnemonic = "ld3"; form = form_3v; break; - case NEON_LD4_post: mnemonic = "ld4"; form = form_4v; break; - case NEON_ST1_1v_post: mnemonic = "st1"; form = form_1v; break; - case NEON_ST1_2v_post: mnemonic = "st1"; form = form_2v; break; - case NEON_ST1_3v_post: mnemonic = "st1"; form = form_3v; break; - case NEON_ST1_4v_post: mnemonic = "st1"; form = form_4v; break; - case NEON_ST2_post: mnemonic = "st2"; form = form_2v; break; - case NEON_ST3_post: mnemonic = "st3"; form = form_3v; break; - case NEON_ST4_post: mnemonic = "st4"; form = form_4v; break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreSingleStruct(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreSingleStruct)"; - - const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns]"; - const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns]"; - const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns]"; - const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns]"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreSingleStructMask)) { - case NEON_LD1_b: mnemonic = "ld1"; form = form_1b; break; - case NEON_LD1_h: mnemonic = "ld1"; form = form_1h; break; - case NEON_LD1_s: - mnemonic = "ld1"; - VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_ST1_b: mnemonic = "st1"; form = form_1b; break; - case NEON_ST1_h: mnemonic = "st1"; form = form_1h; break; - case NEON_ST1_s: - mnemonic = "st1"; - VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_LD1R: - mnemonic = "ld1r"; - form = "{'Vt.%s}, ['Xns]"; - break; - case NEON_LD2_b: - case NEON_ST2_b: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns]"; - break; - case NEON_LD2_h: - case NEON_ST2_h: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns]"; - break; - case NEON_LD2_s: - case NEON_ST2_s: - VIXL_STATIC_ASSERT((NEON_ST2_s | (1 << NEONLSSize_offset)) == NEON_ST2_d); - VIXL_STATIC_ASSERT((NEON_LD2_s | (1 << NEONLSSize_offset)) == NEON_LD2_d); - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns]"; - else - form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns]"; - break; - case NEON_LD2R: - mnemonic = "ld2r"; - form = "{'Vt.%s, 'Vt2.%s}, ['Xns]"; - break; - case NEON_LD3_b: - case NEON_ST3_b: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns]"; - break; - case NEON_LD3_h: - case NEON_ST3_h: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns]"; - break; - case NEON_LD3_s: - case NEON_ST3_s: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns]"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns]"; - break; - case NEON_LD3R: - mnemonic = "ld3r"; - form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns]"; - break; - case NEON_LD4_b: - case NEON_ST4_b: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns]"; - break; - case NEON_LD4_h: - case NEON_ST4_h: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns]"; - break; - case NEON_LD4_s: - case NEON_ST4_s: - VIXL_STATIC_ASSERT((NEON_LD4_s | (1 << NEONLSSize_offset)) == NEON_LD4_d); - VIXL_STATIC_ASSERT((NEON_ST4_s | (1 << NEONLSSize_offset)) == NEON_ST4_d); - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns]"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns]"; - break; - case NEON_LD4R: - mnemonic = "ld4r"; - form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]"; - break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreSingleStructPostIndex( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreSingleStructPostIndex)"; - - const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns], 'Xmb1"; - const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns], 'Xmb2"; - const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns], 'Xmb4"; - const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns], 'Xmb8"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreSingleStructPostIndexMask)) { - case NEON_LD1_b_post: mnemonic = "ld1"; form = form_1b; break; - case NEON_LD1_h_post: mnemonic = "ld1"; form = form_1h; break; - case NEON_LD1_s_post: - mnemonic = "ld1"; - VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_ST1_b_post: mnemonic = "st1"; form = form_1b; break; - case NEON_ST1_h_post: mnemonic = "st1"; form = form_1h; break; - case NEON_ST1_s_post: - mnemonic = "st1"; - VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_LD1R_post: - mnemonic = "ld1r"; - form = "{'Vt.%s}, ['Xns], 'Xmz1"; - break; - case NEON_LD2_b_post: - case NEON_ST2_b_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns], 'Xmb2"; - break; - case NEON_ST2_h_post: - case NEON_LD2_h_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns], 'Xmb4"; - break; - case NEON_LD2_s_post: - case NEON_ST2_s_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns], 'Xmb8"; - else - form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns], 'Xmb16"; - break; - case NEON_LD2R_post: - mnemonic = "ld2r"; - form = "{'Vt.%s, 'Vt2.%s}, ['Xns], 'Xmz2"; - break; - case NEON_LD3_b_post: - case NEON_ST3_b_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns], 'Xmb3"; - break; - case NEON_LD3_h_post: - case NEON_ST3_h_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns], 'Xmb6"; - break; - case NEON_LD3_s_post: - case NEON_ST3_s_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns], 'Xmb12"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns], 'Xmr3"; - break; - case NEON_LD3R_post: - mnemonic = "ld3r"; - form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns], 'Xmz3"; - break; - case NEON_LD4_b_post: - case NEON_ST4_b_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns], 'Xmb4"; - break; - case NEON_LD4_h_post: - case NEON_ST4_h_post: - mnemonic = (instr->LdStXLoad()) == 1 ? "ld4" : "st4"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns], 'Xmb8"; - break; - case NEON_LD4_s_post: - case NEON_ST4_s_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns], 'Xmb16"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns], 'Xmb32"; - break; - case NEON_LD4R_post: - mnemonic = "ld4r"; - form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmz4"; - break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONModifiedImmediate(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vt.%s, 'IVMIImm8, lsl 'IVMIShiftAmt1"; - - int cmode = instr->NEONCmode(); - int cmode_3 = (cmode >> 3) & 1; - int cmode_2 = (cmode >> 2) & 1; - int cmode_1 = (cmode >> 1) & 1; - int cmode_0 = cmode & 1; - int q = instr->NEONQ(); - int op = instr->NEONModImmOp(); - - static const NEONFormatMap map_b = { {30}, {NF_8B, NF_16B} }; - static const NEONFormatMap map_h = { {30}, {NF_4H, NF_8H} }; - static const NEONFormatMap map_s = { {30}, {NF_2S, NF_4S} }; - NEONFormatDecoder nfd(instr, &map_b); - - if (cmode_3 == 0) { - if (cmode_0 == 0) { - mnemonic = (op == 1) ? "mvni" : "movi"; - } else { // cmode<0> == '1'. - mnemonic = (op == 1) ? "bic" : "orr"; - } - nfd.SetFormatMap(0, &map_s); - } else { // cmode<3> == '1'. - if (cmode_2 == 0) { - if (cmode_0 == 0) { - mnemonic = (op == 1) ? "mvni" : "movi"; - } else { // cmode<0> == '1'. - mnemonic = (op == 1) ? "bic" : "orr"; - } - nfd.SetFormatMap(0, &map_h); - } else { // cmode<2> == '1'. - if (cmode_1 == 0) { - mnemonic = (op == 1) ? "mvni" : "movi"; - form = "'Vt.%s, 'IVMIImm8, msl 'IVMIShiftAmt2"; - nfd.SetFormatMap(0, &map_s); - } else { // cmode<1> == '1'. - if (cmode_0 == 0) { - mnemonic = "movi"; - if (op == 0) { - form = "'Vt.%s, 'IVMIImm8"; - } else { - form = (q == 0) ? "'Dd, 'IVMIImm" : "'Vt.2d, 'IVMIImm"; - } - } else { // cmode<0> == '1' - mnemonic = "fmov"; - if (op == 0) { - form = "'Vt.%s, 'IVMIImmFPSingle"; - nfd.SetFormatMap(0, &map_s); - } else { - if (q == 1) { - form = "'Vt.2d, 'IVMIImmFPDouble"; - } - } - } - } - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONScalar2RegMisc(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn"; - const char *form_0 = "%sd, %sn, #0"; - const char *form_fp0 = "%sd, %sn, #0.0"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); - - if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_scalar_opcode) { - // These instructions all use a two bit size field, except NOT and RBIT, - // which use the field to encode the operation. - switch (instr->Mask(NEONScalar2RegMiscMask)) { - case NEON_CMGT_zero_scalar: mnemonic = "cmgt"; form = form_0; break; - case NEON_CMGE_zero_scalar: mnemonic = "cmge"; form = form_0; break; - case NEON_CMLE_zero_scalar: mnemonic = "cmle"; form = form_0; break; - case NEON_CMLT_zero_scalar: mnemonic = "cmlt"; form = form_0; break; - case NEON_CMEQ_zero_scalar: mnemonic = "cmeq"; form = form_0; break; - case NEON_NEG_scalar: mnemonic = "neg"; break; - case NEON_SQNEG_scalar: mnemonic = "sqneg"; break; - case NEON_ABS_scalar: mnemonic = "abs"; break; - case NEON_SQABS_scalar: mnemonic = "sqabs"; break; - case NEON_SUQADD_scalar: mnemonic = "suqadd"; break; - case NEON_USQADD_scalar: mnemonic = "usqadd"; break; - default: form = "(NEONScalar2RegMisc)"; - } - } else { - // These instructions all use a one bit size field, except SQXTUN, SQXTN - // and UQXTN, which use a two bit size field. - nfd.SetFormatMaps(nfd.FPScalarFormatMap()); - switch (instr->Mask(NEONScalar2RegMiscFPMask)) { - case NEON_FRSQRTE_scalar: mnemonic = "frsqrte"; break; - case NEON_FRECPE_scalar: mnemonic = "frecpe"; break; - case NEON_SCVTF_scalar: mnemonic = "scvtf"; break; - case NEON_UCVTF_scalar: mnemonic = "ucvtf"; break; - case NEON_FCMGT_zero_scalar: mnemonic = "fcmgt"; form = form_fp0; break; - case NEON_FCMGE_zero_scalar: mnemonic = "fcmge"; form = form_fp0; break; - case NEON_FCMLE_zero_scalar: mnemonic = "fcmle"; form = form_fp0; break; - case NEON_FCMLT_zero_scalar: mnemonic = "fcmlt"; form = form_fp0; break; - case NEON_FCMEQ_zero_scalar: mnemonic = "fcmeq"; form = form_fp0; break; - case NEON_FRECPX_scalar: mnemonic = "frecpx"; break; - case NEON_FCVTNS_scalar: mnemonic = "fcvtns"; break; - case NEON_FCVTNU_scalar: mnemonic = "fcvtnu"; break; - case NEON_FCVTPS_scalar: mnemonic = "fcvtps"; break; - case NEON_FCVTPU_scalar: mnemonic = "fcvtpu"; break; - case NEON_FCVTMS_scalar: mnemonic = "fcvtms"; break; - case NEON_FCVTMU_scalar: mnemonic = "fcvtmu"; break; - case NEON_FCVTZS_scalar: mnemonic = "fcvtzs"; break; - case NEON_FCVTZU_scalar: mnemonic = "fcvtzu"; break; - case NEON_FCVTAS_scalar: mnemonic = "fcvtas"; break; - case NEON_FCVTAU_scalar: mnemonic = "fcvtau"; break; - case NEON_FCVTXN_scalar: - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - mnemonic = "fcvtxn"; - break; - default: - nfd.SetFormatMap(0, nfd.ScalarFormatMap()); - nfd.SetFormatMap(1, nfd.LongScalarFormatMap()); - switch (instr->Mask(NEONScalar2RegMiscMask)) { - case NEON_SQXTN_scalar: mnemonic = "sqxtn"; break; - case NEON_UQXTN_scalar: mnemonic = "uqxtn"; break; - case NEON_SQXTUN_scalar: mnemonic = "sqxtun"; break; - default: form = "(NEONScalar2RegMisc)"; - } - } - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONScalar3Diff(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, %sm"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LongScalarFormatMap(), - NEONFormatDecoder::ScalarFormatMap()); - - switch (instr->Mask(NEONScalar3DiffMask)) { - case NEON_SQDMLAL_scalar : mnemonic = "sqdmlal"; break; - case NEON_SQDMLSL_scalar : mnemonic = "sqdmlsl"; break; - case NEON_SQDMULL_scalar : mnemonic = "sqdmull"; break; - default: form = "(NEONScalar3Diff)"; - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONScalar3Same(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, %sm"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); - - if (instr->Mask(NEONScalar3SameFPFMask) == NEONScalar3SameFPFixed) { - nfd.SetFormatMaps(nfd.FPScalarFormatMap()); - switch (instr->Mask(NEONScalar3SameFPMask)) { - case NEON_FACGE_scalar: mnemonic = "facge"; break; - case NEON_FACGT_scalar: mnemonic = "facgt"; break; - case NEON_FCMEQ_scalar: mnemonic = "fcmeq"; break; - case NEON_FCMGE_scalar: mnemonic = "fcmge"; break; - case NEON_FCMGT_scalar: mnemonic = "fcmgt"; break; - case NEON_FMULX_scalar: mnemonic = "fmulx"; break; - case NEON_FRECPS_scalar: mnemonic = "frecps"; break; - case NEON_FRSQRTS_scalar: mnemonic = "frsqrts"; break; - case NEON_FABD_scalar: mnemonic = "fabd"; break; - default: form = "(NEONScalar3Same)"; - } - } else { - switch (instr->Mask(NEONScalar3SameMask)) { - case NEON_ADD_scalar: mnemonic = "add"; break; - case NEON_SUB_scalar: mnemonic = "sub"; break; - case NEON_CMEQ_scalar: mnemonic = "cmeq"; break; - case NEON_CMGE_scalar: mnemonic = "cmge"; break; - case NEON_CMGT_scalar: mnemonic = "cmgt"; break; - case NEON_CMHI_scalar: mnemonic = "cmhi"; break; - case NEON_CMHS_scalar: mnemonic = "cmhs"; break; - case NEON_CMTST_scalar: mnemonic = "cmtst"; break; - case NEON_UQADD_scalar: mnemonic = "uqadd"; break; - case NEON_SQADD_scalar: mnemonic = "sqadd"; break; - case NEON_UQSUB_scalar: mnemonic = "uqsub"; break; - case NEON_SQSUB_scalar: mnemonic = "sqsub"; break; - case NEON_USHL_scalar: mnemonic = "ushl"; break; - case NEON_SSHL_scalar: mnemonic = "sshl"; break; - case NEON_UQSHL_scalar: mnemonic = "uqshl"; break; - case NEON_SQSHL_scalar: mnemonic = "sqshl"; break; - case NEON_URSHL_scalar: mnemonic = "urshl"; break; - case NEON_SRSHL_scalar: mnemonic = "srshl"; break; - case NEON_UQRSHL_scalar: mnemonic = "uqrshl"; break; - case NEON_SQRSHL_scalar: mnemonic = "sqrshl"; break; - case NEON_SQDMULH_scalar: mnemonic = "sqdmulh"; break; - case NEON_SQRDMULH_scalar: mnemonic = "sqrdmulh"; break; - default: form = "(NEONScalar3Same)"; - } - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONScalarByIndexedElement(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, 'Ve.%s['IVByElemIndex]"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); - bool long_instr = false; - - switch (instr->Mask(NEONScalarByIndexedElementMask)) { - case NEON_SQDMULL_byelement_scalar: - mnemonic = "sqdmull"; - long_instr = true; - break; - case NEON_SQDMLAL_byelement_scalar: - mnemonic = "sqdmlal"; - long_instr = true; - break; - case NEON_SQDMLSL_byelement_scalar: - mnemonic = "sqdmlsl"; - long_instr = true; - break; - case NEON_SQDMULH_byelement_scalar: - mnemonic = "sqdmulh"; - break; - case NEON_SQRDMULH_byelement_scalar: - mnemonic = "sqrdmulh"; - break; - default: - nfd.SetFormatMap(0, nfd.FPScalarFormatMap()); - switch (instr->Mask(NEONScalarByIndexedElementFPMask)) { - case NEON_FMUL_byelement_scalar: mnemonic = "fmul"; break; - case NEON_FMLA_byelement_scalar: mnemonic = "fmla"; break; - case NEON_FMLS_byelement_scalar: mnemonic = "fmls"; break; - case NEON_FMULX_byelement_scalar: mnemonic = "fmulx"; break; - default: form = "(NEONScalarByIndexedElement)"; - } - } - - if (long_instr) { - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - } - - Format(instr, mnemonic, nfd.Substitute( - form, nfd.kPlaceholder, nfd.kPlaceholder, nfd.kFormat)); -} - - -void Disassembler::VisitNEONScalarCopy(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONScalarCopy)"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularScalarFormatMap()); - - if (instr->Mask(NEONScalarCopyMask) == NEON_DUP_ELEMENT_scalar) { - mnemonic = "mov"; - form = "%sd, 'Vn.%s['IVInsIndex1]"; - } - - Format(instr, mnemonic, nfd.Substitute(form, nfd.kPlaceholder, nfd.kFormat)); -} - - -void Disassembler::VisitNEONScalarPairwise(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, 'Vn.%s"; - NEONFormatMap map = { {22}, {NF_2S, NF_2D} }; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPScalarFormatMap(), &map); - - switch (instr->Mask(NEONScalarPairwiseMask)) { - case NEON_ADDP_scalar: mnemonic = "addp"; break; - case NEON_FADDP_scalar: mnemonic = "faddp"; break; - case NEON_FMAXP_scalar: mnemonic = "fmaxp"; break; - case NEON_FMAXNMP_scalar: mnemonic = "fmaxnmp"; break; - case NEON_FMINP_scalar: mnemonic = "fminp"; break; - case NEON_FMINNMP_scalar: mnemonic = "fminnmp"; break; - default: form = "(NEONScalarPairwise)"; - } - Format(instr, mnemonic, nfd.Substitute(form, - NEONFormatDecoder::kPlaceholder, NEONFormatDecoder::kFormat)); -} - - -void Disassembler::VisitNEONScalarShiftImmediate(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, 'Is1"; - const char *form_2 = "%sd, %sn, 'Is2"; - - static const NEONFormatMap map_shift = { - {22, 21, 20, 19}, - {NF_UNDEF, NF_B, NF_H, NF_H, NF_S, NF_S, NF_S, NF_S, - NF_D, NF_D, NF_D, NF_D, NF_D, NF_D, NF_D, NF_D} - }; - static const NEONFormatMap map_shift_narrow = { - {21, 20, 19}, - {NF_UNDEF, NF_H, NF_S, NF_S, NF_D, NF_D, NF_D, NF_D} - }; - NEONFormatDecoder nfd(instr, &map_shift); - - if (instr->ImmNEONImmh()) { // immh has to be non-zero. - switch (instr->Mask(NEONScalarShiftImmediateMask)) { - case NEON_FCVTZU_imm_scalar: mnemonic = "fcvtzu"; break; - case NEON_FCVTZS_imm_scalar: mnemonic = "fcvtzs"; break; - case NEON_SCVTF_imm_scalar: mnemonic = "scvtf"; break; - case NEON_UCVTF_imm_scalar: mnemonic = "ucvtf"; break; - case NEON_SRI_scalar: mnemonic = "sri"; break; - case NEON_SSHR_scalar: mnemonic = "sshr"; break; - case NEON_USHR_scalar: mnemonic = "ushr"; break; - case NEON_SRSHR_scalar: mnemonic = "srshr"; break; - case NEON_URSHR_scalar: mnemonic = "urshr"; break; - case NEON_SSRA_scalar: mnemonic = "ssra"; break; - case NEON_USRA_scalar: mnemonic = "usra"; break; - case NEON_SRSRA_scalar: mnemonic = "srsra"; break; - case NEON_URSRA_scalar: mnemonic = "ursra"; break; - case NEON_SHL_scalar: mnemonic = "shl"; form = form_2; break; - case NEON_SLI_scalar: mnemonic = "sli"; form = form_2; break; - case NEON_SQSHLU_scalar: mnemonic = "sqshlu"; form = form_2; break; - case NEON_SQSHL_imm_scalar: mnemonic = "sqshl"; form = form_2; break; - case NEON_UQSHL_imm_scalar: mnemonic = "uqshl"; form = form_2; break; - case NEON_UQSHRN_scalar: - mnemonic = "uqshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_UQRSHRN_scalar: - mnemonic = "uqrshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQSHRN_scalar: - mnemonic = "sqshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQRSHRN_scalar: - mnemonic = "sqrshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQSHRUN_scalar: - mnemonic = "sqshrun"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQRSHRUN_scalar: - mnemonic = "sqrshrun"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - default: - form = "(NEONScalarShiftImmediate)"; - } - } else { - form = "(NEONScalarShiftImmediate)"; - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONShiftImmediate(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Is1"; - const char *form_shift_2 = "'Vd.%s, 'Vn.%s, 'Is2"; - const char *form_xtl = "'Vd.%s, 'Vn.%s"; - - // 0001->8H, 001x->4S, 01xx->2D, all others undefined. - static const NEONFormatMap map_shift_ta = { - {22, 21, 20, 19}, - {NF_UNDEF, NF_8H, NF_4S, NF_4S, NF_2D, NF_2D, NF_2D, NF_2D} - }; - - // 00010->8B, 00011->16B, 001x0->4H, 001x1->8H, - // 01xx0->2S, 01xx1->4S, 1xxx1->2D, all others undefined. - static const NEONFormatMap map_shift_tb = { - {22, 21, 20, 19, 30}, - {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_4H, NF_8H, - NF_2S, NF_4S, NF_2S, NF_4S, NF_2S, NF_4S, NF_2S, NF_4S, - NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, - NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D} - }; - - NEONFormatDecoder nfd(instr, &map_shift_tb); - - if (instr->ImmNEONImmh()) { // immh has to be non-zero. - switch (instr->Mask(NEONShiftImmediateMask)) { - case NEON_SQSHLU: mnemonic = "sqshlu"; form = form_shift_2; break; - case NEON_SQSHL_imm: mnemonic = "sqshl"; form = form_shift_2; break; - case NEON_UQSHL_imm: mnemonic = "uqshl"; form = form_shift_2; break; - case NEON_SHL: mnemonic = "shl"; form = form_shift_2; break; - case NEON_SLI: mnemonic = "sli"; form = form_shift_2; break; - case NEON_SCVTF_imm: mnemonic = "scvtf"; break; - case NEON_UCVTF_imm: mnemonic = "ucvtf"; break; - case NEON_FCVTZU_imm: mnemonic = "fcvtzu"; break; - case NEON_FCVTZS_imm: mnemonic = "fcvtzs"; break; - case NEON_SRI: mnemonic = "sri"; break; - case NEON_SSHR: mnemonic = "sshr"; break; - case NEON_USHR: mnemonic = "ushr"; break; - case NEON_SRSHR: mnemonic = "srshr"; break; - case NEON_URSHR: mnemonic = "urshr"; break; - case NEON_SSRA: mnemonic = "ssra"; break; - case NEON_USRA: mnemonic = "usra"; break; - case NEON_SRSRA: mnemonic = "srsra"; break; - case NEON_URSRA: mnemonic = "ursra"; break; - case NEON_SHRN: - mnemonic = instr->Mask(NEON_Q) ? "shrn2" : "shrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_RSHRN: - mnemonic = instr->Mask(NEON_Q) ? "rshrn2" : "rshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_UQSHRN: - mnemonic = instr->Mask(NEON_Q) ? "uqshrn2" : "uqshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_UQRSHRN: - mnemonic = instr->Mask(NEON_Q) ? "uqrshrn2" : "uqrshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQSHRN: - mnemonic = instr->Mask(NEON_Q) ? "sqshrn2" : "sqshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQRSHRN: - mnemonic = instr->Mask(NEON_Q) ? "sqrshrn2" : "sqrshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQSHRUN: - mnemonic = instr->Mask(NEON_Q) ? "sqshrun2" : "sqshrun"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQRSHRUN: - mnemonic = instr->Mask(NEON_Q) ? "sqrshrun2" : "sqrshrun"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SSHLL: - nfd.SetFormatMap(0, &map_shift_ta); - if (instr->ImmNEONImmb() == 0 && - CountSetBits(instr->ImmNEONImmh(), 32) == 1) { // sxtl variant. - form = form_xtl; - mnemonic = instr->Mask(NEON_Q) ? "sxtl2" : "sxtl"; - } else { // sshll variant. - form = form_shift_2; - mnemonic = instr->Mask(NEON_Q) ? "sshll2" : "sshll"; - } - break; - case NEON_USHLL: - nfd.SetFormatMap(0, &map_shift_ta); - if (instr->ImmNEONImmb() == 0 && - CountSetBits(instr->ImmNEONImmh(), 32) == 1) { // uxtl variant. - form = form_xtl; - mnemonic = instr->Mask(NEON_Q) ? "uxtl2" : "uxtl"; - } else { // ushll variant. - form = form_shift_2; - mnemonic = instr->Mask(NEON_Q) ? "ushll2" : "ushll"; - } - break; - default: form = "(NEONShiftImmediate)"; - } - } else { - form = "(NEONShiftImmediate)"; - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONTable(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONTable)"; - const char form_1v[] = "'Vd.%%s, {'Vn.16b}, 'Vm.%%s"; - const char form_2v[] = "'Vd.%%s, {'Vn.16b, v%d.16b}, 'Vm.%%s"; - const char form_3v[] = "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b}, 'Vm.%%s"; - const char form_4v[] = - "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b, v%d.16b}, 'Vm.%%s"; - static const NEONFormatMap map_b = { {30}, {NF_8B, NF_16B} }; - NEONFormatDecoder nfd(instr, &map_b); - - switch (instr->Mask(NEONTableMask)) { - case NEON_TBL_1v: mnemonic = "tbl"; form = form_1v; break; - case NEON_TBL_2v: mnemonic = "tbl"; form = form_2v; break; - case NEON_TBL_3v: mnemonic = "tbl"; form = form_3v; break; - case NEON_TBL_4v: mnemonic = "tbl"; form = form_4v; break; - case NEON_TBX_1v: mnemonic = "tbx"; form = form_1v; break; - case NEON_TBX_2v: mnemonic = "tbx"; form = form_2v; break; - case NEON_TBX_3v: mnemonic = "tbx"; form = form_3v; break; - case NEON_TBX_4v: mnemonic = "tbx"; form = form_4v; break; - default: break; - } - - char re_form[sizeof(form_4v) + 6]; - int reg_num = instr->Rn(); - snprintf(re_form, sizeof(re_form), form, - (reg_num + 1) % kNumberOfVRegisters, - (reg_num + 2) % kNumberOfVRegisters, - (reg_num + 3) % kNumberOfVRegisters); - - Format(instr, mnemonic, nfd.Substitute(re_form)); -} - - -void Disassembler::VisitNEONPerm(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; - NEONFormatDecoder nfd(instr); - - switch (instr->Mask(NEONPermMask)) { - case NEON_TRN1: mnemonic = "trn1"; break; - case NEON_TRN2: mnemonic = "trn2"; break; - case NEON_UZP1: mnemonic = "uzp1"; break; - case NEON_UZP2: mnemonic = "uzp2"; break; - case NEON_ZIP1: mnemonic = "zip1"; break; - case NEON_ZIP2: mnemonic = "zip2"; break; - default: form = "(NEONPerm)"; - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitUnimplemented(const Instruction* instr) { - Format(instr, "unimplemented", "(Unimplemented)"); -} - - -void Disassembler::VisitUnallocated(const Instruction* instr) { - Format(instr, "unallocated", "(Unallocated)"); -} - - -void Disassembler::ProcessOutput(const Instruction* /*instr*/) { - // The base disasm does nothing more than disassembling into a buffer. -} - - -void Disassembler::AppendRegisterNameToOutput(const Instruction* instr, - const CPURegister& reg) { - USE(instr); - VIXL_ASSERT(reg.IsValid()); - char reg_char; - - if (reg.IsRegister()) { - reg_char = reg.Is64Bits() ? 'x' : 'w'; - } else { - VIXL_ASSERT(reg.IsVRegister()); - switch (reg.SizeInBits()) { - case kBRegSize: reg_char = 'b'; break; - case kHRegSize: reg_char = 'h'; break; - case kSRegSize: reg_char = 's'; break; - case kDRegSize: reg_char = 'd'; break; - default: - VIXL_ASSERT(reg.Is128Bits()); - reg_char = 'q'; - } - } - - if (reg.IsVRegister() || !(reg.Aliases(sp) || reg.Aliases(xzr))) { - // A core or scalar/vector register: [wx]0 - 30, [bhsdq]0 - 31. - AppendToOutput("%c%d", reg_char, reg.code()); - } else if (reg.Aliases(sp)) { - // Disassemble w31/x31 as stack pointer wsp/sp. - AppendToOutput("%s", reg.Is64Bits() ? "sp" : "wsp"); - } else { - // Disassemble w31/x31 as zero register wzr/xzr. - AppendToOutput("%czr", reg_char); - } -} - - -void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, - int64_t offset) { - USE(instr); - uint64_t abs_offset = offset; - char sign = (offset < 0) ? '-' : '+'; - if (offset < 0) { - abs_offset = -abs_offset; - } - AppendToOutput("#%c0x%" PRIx64, sign, abs_offset); -} - - -void Disassembler::AppendAddressToOutput(const Instruction* instr, - const void* addr) { - USE(instr); - AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast(addr)); -} - - -void Disassembler::AppendCodeAddressToOutput(const Instruction* instr, - const void* addr) { - AppendAddressToOutput(instr, addr); -} - - -void Disassembler::AppendDataAddressToOutput(const Instruction* instr, - const void* addr) { - AppendAddressToOutput(instr, addr); -} - - -void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction* instr, - const void* addr) { - USE(instr); - int64_t rel_addr = CodeRelativeAddress(addr); - if (rel_addr >= 0) { - AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr); - } else { - AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr); - } -} - - -void Disassembler::AppendCodeRelativeCodeAddressToOutput( - const Instruction* instr, const void* addr) { - AppendCodeRelativeAddressToOutput(instr, addr); -} - - -void Disassembler::AppendCodeRelativeDataAddressToOutput( - const Instruction* instr, const void* addr) { - AppendCodeRelativeAddressToOutput(instr, addr); -} - - -void Disassembler::MapCodeAddress(int64_t base_address, - const Instruction* instr_address) { - set_code_address_offset( - base_address - reinterpret_cast(instr_address)); -} -int64_t Disassembler::CodeRelativeAddress(const void* addr) { - return reinterpret_cast(addr) + code_address_offset(); -} - - -void Disassembler::Format(const Instruction* instr, const char* mnemonic, - const char* format) { - VIXL_ASSERT(mnemonic != NULL); - ResetOutput(); - Substitute(instr, mnemonic); - if (format != NULL) { - VIXL_ASSERT(buffer_pos_ < buffer_size_); - buffer_[buffer_pos_++] = ' '; - Substitute(instr, format); - } - VIXL_ASSERT(buffer_pos_ < buffer_size_); - buffer_[buffer_pos_] = 0; - ProcessOutput(instr); -} - - -void Disassembler::Substitute(const Instruction* instr, const char* string) { - char chr = *string++; - while (chr != '\0') { - if (chr == '\'') { - string += SubstituteField(instr, string); - } else { - VIXL_ASSERT(buffer_pos_ < buffer_size_); - buffer_[buffer_pos_++] = chr; - } - chr = *string++; - } -} - - -int Disassembler::SubstituteField(const Instruction* instr, - const char* format) { - switch (format[0]) { - // NB. The remaining substitution prefix characters are: GJKUZ. - case 'R': // Register. X or W, selected by sf bit. - case 'F': // FP register. S or D, selected by type field. - case 'V': // Vector register, V, vector format. - case 'W': - case 'X': - case 'B': - case 'H': - case 'S': - case 'D': - case 'Q': return SubstituteRegisterField(instr, format); - case 'I': return SubstituteImmediateField(instr, format); - case 'L': return SubstituteLiteralField(instr, format); - case 'N': return SubstituteShiftField(instr, format); - case 'P': return SubstitutePrefetchField(instr, format); - case 'C': return SubstituteConditionField(instr, format); - case 'E': return SubstituteExtendField(instr, format); - case 'A': return SubstitutePCRelAddressField(instr, format); - case 'T': return SubstituteBranchTargetField(instr, format); - case 'O': return SubstituteLSRegOffsetField(instr, format); - case 'M': return SubstituteBarrierField(instr, format); - case 'K': return SubstituteCrField(instr, format); - case 'G': return SubstituteSysOpField(instr, format); - default: { - VIXL_UNREACHABLE(); - return 1; - } - } -} - - -int Disassembler::SubstituteRegisterField(const Instruction* instr, - const char* format) { - char reg_prefix = format[0]; - unsigned reg_num = 0; - unsigned field_len = 2; - - switch (format[1]) { - case 'd': - reg_num = instr->Rd(); - if (format[2] == 'q') { - reg_prefix = instr->NEONQ() ? 'X' : 'W'; - field_len = 3; - } - break; - case 'n': reg_num = instr->Rn(); break; - case 'm': - reg_num = instr->Rm(); - switch (format[2]) { - // Handle registers tagged with b (bytes), z (instruction), or - // r (registers), used for address updates in - // NEON load/store instructions. - case 'r': - case 'b': - case 'z': { - field_len = 3; - char* eimm; - int imm = static_cast(strtol(&format[3], &eimm, 10)); - field_len += eimm - &format[3]; - if (reg_num == 31) { - switch (format[2]) { - case 'z': - imm *= (1 << instr->NEONLSSize()); - break; - case 'r': - imm *= (instr->NEONQ() == 0) ? kDRegSizeInBytes - : kQRegSizeInBytes; - break; - case 'b': - break; - } - AppendToOutput("#%d", imm); - return field_len; - } - break; - } - } - break; - case 'e': - // This is register Rm, but using a 4-bit specifier. Used in NEON - // by-element instructions. - reg_num = (instr->Rm() & 0xf); - break; - case 'a': reg_num = instr->Ra(); break; - case 's': reg_num = instr->Rs(); break; - case 't': - reg_num = instr->Rt(); - if (format[0] == 'V') { - if ((format[2] >= '2') && (format[2] <= '4')) { - // Handle consecutive vector register specifiers Vt2, Vt3 and Vt4. - reg_num = (reg_num + format[2] - '1') % 32; - field_len = 3; - } - } else { - if (format[2] == '2') { - // Handle register specifier Rt2. - reg_num = instr->Rt2(); - field_len = 3; - } - } - break; - default: VIXL_UNREACHABLE(); - } - - // Increase field length for registers tagged as stack. - if (format[2] == 's') { - field_len = 3; - } - - CPURegister::RegisterType reg_type = CPURegister::kRegister; - unsigned reg_size = kXRegSize; - - if (reg_prefix == 'R') { - reg_prefix = instr->SixtyFourBits() ? 'X' : 'W'; - } else if (reg_prefix == 'F') { - reg_prefix = ((instr->FPType() & 1) == 0) ? 'S' : 'D'; - } - - switch (reg_prefix) { - case 'W': - reg_type = CPURegister::kRegister; reg_size = kWRegSize; break; - case 'X': - reg_type = CPURegister::kRegister; reg_size = kXRegSize; break; - case 'B': - reg_type = CPURegister::kVRegister; reg_size = kBRegSize; break; - case 'H': - reg_type = CPURegister::kVRegister; reg_size = kHRegSize; break; - case 'S': - reg_type = CPURegister::kVRegister; reg_size = kSRegSize; break; - case 'D': - reg_type = CPURegister::kVRegister; reg_size = kDRegSize; break; - case 'Q': - reg_type = CPURegister::kVRegister; reg_size = kQRegSize; break; - case 'V': - AppendToOutput("v%d", reg_num); - return field_len; - default: - VIXL_UNREACHABLE(); - } - - if ((reg_type == CPURegister::kRegister) && - (reg_num == kZeroRegCode) && (format[2] == 's')) { - reg_num = kSPRegInternalCode; - } - - AppendRegisterNameToOutput(instr, CPURegister(reg_num, reg_size, reg_type)); - - return field_len; -} - - -int Disassembler::SubstituteImmediateField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'I'); - - switch (format[1]) { - case 'M': { // IMoveImm, IMoveNeg or IMoveLSL. - if (format[5] == 'L') { - AppendToOutput("#0x%" PRIx32, instr->ImmMoveWide()); - if (instr->ShiftMoveWide() > 0) { - AppendToOutput(", lsl #%" PRId32, 16 * instr->ShiftMoveWide()); - } - } else { - VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N')); - uint64_t imm = static_cast(instr->ImmMoveWide()) << - (16 * instr->ShiftMoveWide()); - if (format[5] == 'N') - imm = ~imm; - if (!instr->SixtyFourBits()) - imm &= UINT64_C(0xffffffff); - AppendToOutput("#0x%" PRIx64, imm); - } - return 8; - } - case 'L': { - switch (format[2]) { - case 'L': { // ILLiteral - Immediate Load Literal. - AppendToOutput("pc%+" PRId32, - instr->ImmLLiteral() << kLiteralEntrySizeLog2); - return 9; - } - case 'S': { // ILS - Immediate Load/Store. - if (instr->ImmLS() != 0) { - AppendToOutput(", #%" PRId32, instr->ImmLS()); - } - return 3; - } - case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. - if (instr->ImmLSPair() != 0) { - // format[3] is the scale value. Convert to a number. - int scale = 1 << (format[3] - '0'); - AppendToOutput(", #%" PRId32, instr->ImmLSPair() * scale); - } - return 4; - } - case 'U': { // ILU - Immediate Load/Store Unsigned. - if (instr->ImmLSUnsigned() != 0) { - int shift = instr->SizeLS(); - AppendToOutput(", #%" PRId32, instr->ImmLSUnsigned() << shift); - } - return 3; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - } - case 'C': { // ICondB - Immediate Conditional Branch. - int64_t offset = instr->ImmCondBranch() << 2; - AppendPCRelativeOffsetToOutput(instr, offset); - return 6; - } - case 'A': { // IAddSub. - VIXL_ASSERT(instr->ShiftAddSub() <= 1); - int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); - AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); - return 7; - } - case 'F': { // IFPSingle, IFPDouble or IFPFBits. - if (format[3] == 'F') { // IFPFbits. - AppendToOutput("#%" PRId32, 64 - instr->FPScale()); - return 8; - } else { - AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmFP(), - format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); - return 9; - } - } - case 'T': { // ITri - Immediate Triangular Encoded. - AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); - return 4; - } - case 'N': { // INzcv. - int nzcv = (instr->Nzcv() << Flags_offset); - AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', - ((nzcv & ZFlag) == 0) ? 'z' : 'Z', - ((nzcv & CFlag) == 0) ? 'c' : 'C', - ((nzcv & VFlag) == 0) ? 'v' : 'V'); - return 5; - } - case 'P': { // IP - Conditional compare. - AppendToOutput("#%" PRId32, instr->ImmCondCmp()); - return 2; - } - case 'B': { // Bitfields. - return SubstituteBitfieldImmediateField(instr, format); - } - case 'E': { // IExtract. - AppendToOutput("#%" PRId32, instr->ImmS()); - return 8; - } - case 'S': { // IS - Test and branch bit. - AppendToOutput("#%" PRId32, (instr->ImmTestBranchBit5() << 5) | - instr->ImmTestBranchBit40()); - return 2; - } - case 's': { // Is - Shift (immediate). - switch (format[2]) { - case '1': { // Is1 - SSHR. - int shift = 16 << HighestSetBitPosition(instr->ImmNEONImmh()); - shift -= instr->ImmNEONImmhImmb(); - AppendToOutput("#%d", shift); - return 3; - } - case '2': { // Is2 - SLI. - int shift = instr->ImmNEONImmhImmb(); - shift -= 8 << HighestSetBitPosition(instr->ImmNEONImmh()); - AppendToOutput("#%d", shift); - return 3; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - } - case 'D': { // IDebug - HLT and BRK instructions. - AppendToOutput("#0x%" PRIx32, instr->ImmException()); - return 6; - } - case 'V': { // Immediate Vector. - switch (format[2]) { - case 'E': { // IVExtract. - AppendToOutput("#%" PRId32, instr->ImmNEONExt()); - return 9; - } - case 'B': { // IVByElemIndex. - int vm_index = (instr->NEONH() << 1) | instr->NEONL(); - if (instr->NEONSize() == 1) { - vm_index = (vm_index << 1) | instr->NEONM(); - } - AppendToOutput("%d", vm_index); - return strlen("IVByElemIndex"); - } - case 'I': { // INS element. - if (strncmp(format, "IVInsIndex", strlen("IVInsIndex")) == 0) { - int rd_index, rn_index; - int imm5 = instr->ImmNEON5(); - int imm4 = instr->ImmNEON4(); - int tz = CountTrailingZeros(imm5, 32); - rd_index = imm5 >> (tz + 1); - rn_index = imm4 >> tz; - if (strncmp(format, "IVInsIndex1", strlen("IVInsIndex1")) == 0) { - AppendToOutput("%d", rd_index); - return strlen("IVInsIndex1"); - } else if (strncmp(format, "IVInsIndex2", - strlen("IVInsIndex2")) == 0) { - AppendToOutput("%d", rn_index); - return strlen("IVInsIndex2"); - } else { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - VIXL_FALLTHROUGH(); - } - case 'L': { // IVLSLane[0123] - suffix indicates access size shift. - AppendToOutput("%d", instr->NEONLSIndex(format[8] - '0')); - return 9; - } - case 'M': { // Modified Immediate cases. - if (strncmp(format, - "IVMIImmFPSingle", - strlen("IVMIImmFPSingle")) == 0) { - AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmNEONabcdefgh(), - instr->ImmNEONFP32()); - return strlen("IVMIImmFPSingle"); - } else if (strncmp(format, - "IVMIImmFPDouble", - strlen("IVMIImmFPDouble")) == 0) { - AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmNEONabcdefgh(), - instr->ImmNEONFP64()); - return strlen("IVMIImmFPDouble"); - } else if (strncmp(format, "IVMIImm8", strlen("IVMIImm8")) == 0) { - uint64_t imm8 = instr->ImmNEONabcdefgh(); - AppendToOutput("#0x%" PRIx64, imm8); - return strlen("IVMIImm8"); - } else if (strncmp(format, "IVMIImm", strlen("IVMIImm")) == 0) { - uint64_t imm8 = instr->ImmNEONabcdefgh(); - uint64_t imm = 0; - for (int i = 0; i < 8; ++i) { - if (imm8 & (1 << i)) { - imm |= (UINT64_C(0xff) << (8 * i)); - } - } - AppendToOutput("#0x%" PRIx64, imm); - return strlen("IVMIImm"); - } else if (strncmp(format, "IVMIShiftAmt1", - strlen("IVMIShiftAmt1")) == 0) { - int cmode = instr->NEONCmode(); - int shift_amount = 8 * ((cmode >> 1) & 3); - AppendToOutput("#%d", shift_amount); - return strlen("IVMIShiftAmt1"); - } else if (strncmp(format, "IVMIShiftAmt2", - strlen("IVMIShiftAmt2")) == 0) { - int cmode = instr->NEONCmode(); - int shift_amount = 8 << (cmode & 1); - AppendToOutput("#%d", shift_amount); - return strlen("IVMIShiftAmt2"); - } else { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - } - case 'X': { // IX - CLREX instruction. - AppendToOutput("#0x%" PRIx32, instr->CRm()); - return 2; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } -} - - -int Disassembler::SubstituteBitfieldImmediateField(const Instruction* instr, - const char* format) { - VIXL_ASSERT((format[0] == 'I') && (format[1] == 'B')); - unsigned r = instr->ImmR(); - unsigned s = instr->ImmS(); - - switch (format[2]) { - case 'r': { // IBr. - AppendToOutput("#%d", r); - return 3; - } - case 's': { // IBs+1 or IBs-r+1. - if (format[3] == '+') { - AppendToOutput("#%d", s + 1); - return 5; - } else { - VIXL_ASSERT(format[3] == '-'); - AppendToOutput("#%d", s - r + 1); - return 7; - } - } - case 'Z': { // IBZ-r. - VIXL_ASSERT((format[3] == '-') && (format[4] == 'r')); - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; - AppendToOutput("#%d", reg_size - r); - return 5; - } - default: { - VIXL_UNREACHABLE(); - return 0; - } - } -} - - -int Disassembler::SubstituteLiteralField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "LValue", 6) == 0); - USE(format); - - const void * address = instr->LiteralAddress(); - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: - case LDR_x_lit: - case LDRSW_x_lit: - case LDR_s_lit: - case LDR_d_lit: - case LDR_q_lit: - AppendCodeRelativeDataAddressToOutput(instr, address); - break; - case PRFM_lit: { - // Use the prefetch hint to decide how to print the address. - switch (instr->PrefetchHint()) { - case 0x0: // PLD: prefetch for load. - case 0x2: // PST: prepare for store. - AppendCodeRelativeDataAddressToOutput(instr, address); - break; - case 0x1: // PLI: preload instructions. - AppendCodeRelativeCodeAddressToOutput(instr, address); - break; - case 0x3: // Unallocated hint. - AppendCodeRelativeAddressToOutput(instr, address); - break; - } - break; - } - default: - VIXL_UNREACHABLE(); - } - - return 6; -} - - -int Disassembler::SubstituteShiftField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'N'); - VIXL_ASSERT(instr->ShiftDP() <= 0x3); - - switch (format[1]) { - case 'D': { // HDP. - VIXL_ASSERT(instr->ShiftDP() != ROR); - VIXL_FALLTHROUGH(); - } - case 'L': { // HLo. - if (instr->ImmDPShift() != 0) { - const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; - AppendToOutput(", %s #%" PRId32, shift_type[instr->ShiftDP()], - instr->ImmDPShift()); - } - return 3; - } - default: - VIXL_UNIMPLEMENTED(); - return 0; - } -} - - -int Disassembler::SubstituteConditionField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'C'); - const char* condition_code[] = { "eq", "ne", "hs", "lo", - "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", - "gt", "le", "al", "nv" }; - int cond; - switch (format[1]) { - case 'B': cond = instr->ConditionBranch(); break; - case 'I': { - cond = InvertCondition(static_cast(instr->Condition())); - break; - } - default: cond = instr->Condition(); - } - AppendToOutput("%s", condition_code[cond]); - return 4; -} - - -int Disassembler::SubstitutePCRelAddressField(const Instruction* instr, - const char* format) { - VIXL_ASSERT((strcmp(format, "AddrPCRelByte") == 0) || // Used by `adr`. - (strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`. - - int64_t offset = instr->ImmPCRel(); - - // Compute the target address based on the effective address (after applying - // code_address_offset). This is required for correct behaviour of adrp. - const Instruction* base = instr + code_address_offset(); - if (format[9] == 'P') { - offset *= kPageSize; - base = AlignDown(base, kPageSize); - } - // Strip code_address_offset before printing, so we can use the - // semantically-correct AppendCodeRelativeAddressToOutput. - const void* target = - reinterpret_cast(base + offset - code_address_offset()); - - AppendPCRelativeOffsetToOutput(instr, offset); - AppendToOutput(" "); - AppendCodeRelativeAddressToOutput(instr, target); - return 13; -} - - -int Disassembler::SubstituteBranchTargetField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "TImm", 4) == 0); - - int64_t offset = 0; - switch (format[5]) { - // BImmUncn - unconditional branch immediate. - case 'n': offset = instr->ImmUncondBranch(); break; - // BImmCond - conditional branch immediate. - case 'o': offset = instr->ImmCondBranch(); break; - // BImmCmpa - compare and branch immediate. - case 'm': offset = instr->ImmCmpBranch(); break; - // BImmTest - test and branch immediate. - case 'e': offset = instr->ImmTestBranch(); break; - default: VIXL_UNIMPLEMENTED(); - } - offset <<= kInstructionSizeLog2; - const void* target_address = reinterpret_cast(instr + offset); - VIXL_STATIC_ASSERT(sizeof(*instr) == 1); - - AppendPCRelativeOffsetToOutput(instr, offset); - AppendToOutput(" "); - AppendCodeRelativeCodeAddressToOutput(instr, target_address); - - return 8; -} - - -int Disassembler::SubstituteExtendField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "Ext", 3) == 0); - VIXL_ASSERT(instr->ExtendMode() <= 7); - USE(format); - - const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", - "sxtb", "sxth", "sxtw", "sxtx" }; - - // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit - // registers becomes lsl. - if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && - (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || - (instr->ExtendMode() == UXTX))) { - if (instr->ImmExtendShift() > 0) { - AppendToOutput(", lsl #%" PRId32, instr->ImmExtendShift()); - } - } else { - AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); - if (instr->ImmExtendShift() > 0) { - AppendToOutput(" #%" PRId32, instr->ImmExtendShift()); - } - } - return 3; -} - - -int Disassembler::SubstituteLSRegOffsetField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "Offsetreg", 9) == 0); - const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", - "undefined", "undefined", "sxtw", "sxtx" }; - USE(format); - - unsigned shift = instr->ImmShiftLS(); - Extend ext = static_cast(instr->ExtendMode()); - char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; - - unsigned rm = instr->Rm(); - if (rm == kZeroRegCode) { - AppendToOutput("%czr", reg_type); - } else { - AppendToOutput("%c%d", reg_type, rm); - } - - // Extend mode UXTX is an alias for shift mode LSL here. - if (!((ext == UXTX) && (shift == 0))) { - AppendToOutput(", %s", extend_mode[ext]); - if (shift != 0) { - AppendToOutput(" #%d", instr->SizeLS()); - } - } - return 9; -} - - -int Disassembler::SubstitutePrefetchField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'P'); - USE(format); - - static const char* hints[] = {"ld", "li", "st"}; - static const char* stream_options[] = {"keep", "strm"}; - - unsigned hint = instr->PrefetchHint(); - unsigned target = instr->PrefetchTarget() + 1; - unsigned stream = instr->PrefetchStream(); - - if ((hint >= (sizeof(hints) / sizeof(hints[0]))) || (target > 3)) { - // Unallocated prefetch operations. - int prefetch_mode = instr->ImmPrefetchOperation(); - AppendToOutput("#0b%c%c%c%c%c", - (prefetch_mode & (1 << 4)) ? '1' : '0', - (prefetch_mode & (1 << 3)) ? '1' : '0', - (prefetch_mode & (1 << 2)) ? '1' : '0', - (prefetch_mode & (1 << 1)) ? '1' : '0', - (prefetch_mode & (1 << 0)) ? '1' : '0'); - } else { - VIXL_ASSERT(stream < (sizeof(stream_options) / sizeof(stream_options[0]))); - AppendToOutput("p%sl%d%s", hints[hint], target, stream_options[stream]); - } - return 6; -} - -int Disassembler::SubstituteBarrierField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'M'); - USE(format); - - static const char* options[4][4] = { - { "sy (0b0000)", "oshld", "oshst", "osh" }, - { "sy (0b0100)", "nshld", "nshst", "nsh" }, - { "sy (0b1000)", "ishld", "ishst", "ish" }, - { "sy (0b1100)", "ld", "st", "sy" } - }; - int domain = instr->ImmBarrierDomain(); - int type = instr->ImmBarrierType(); - - AppendToOutput("%s", options[domain][type]); - return 1; -} - -int Disassembler::SubstituteSysOpField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'G'); - int op = -1; - switch (format[1]) { - case '1': op = instr->SysOp1(); break; - case '2': op = instr->SysOp2(); break; - default: - VIXL_UNREACHABLE(); - } - AppendToOutput("#%d", op); - return 2; -} - -int Disassembler::SubstituteCrField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'K'); - int cr = -1; - switch (format[1]) { - case 'n': cr = instr->CRn(); break; - case 'm': cr = instr->CRm(); break; - default: - VIXL_UNREACHABLE(); - } - AppendToOutput("C%d", cr); - return 2; -} - -void Disassembler::ResetOutput() { - buffer_pos_ = 0; - buffer_[buffer_pos_] = 0; -} - - -void Disassembler::AppendToOutput(const char* format, ...) { - va_list args; - va_start(args, format); - buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_ - buffer_pos_, - format, args); - va_end(args); -} - - -void PrintDisassembler::ProcessOutput(const Instruction* instr) { - fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", - reinterpret_cast(instr), - instr->InstructionBits(), - GetOutput()); -} - -} // namespace vixl diff --git a/disas/libvixl/vixl/a64/disasm-a64.h b/disas/libvixl/vixl/a64/disasm-a64.h deleted file mode 100644 index 930df6ea6a5..00000000000 --- a/disas/libvixl/vixl/a64/disasm-a64.h +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_DISASM_A64_H -#define VIXL_A64_DISASM_A64_H - -#include "vixl/globals.h" -#include "vixl/utils.h" -#include "vixl/a64/instructions-a64.h" -#include "vixl/a64/decoder-a64.h" -#include "vixl/a64/assembler-a64.h" - -namespace vixl { - -class Disassembler: public DecoderVisitor { - public: - Disassembler(); - Disassembler(char* text_buffer, int buffer_size); - virtual ~Disassembler(); - char* GetOutput(); - - // Declare all Visitor functions. - #define DECLARE(A) virtual void Visit##A(const Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - protected: - virtual void ProcessOutput(const Instruction* instr); - - // Default output functions. The functions below implement a default way of - // printing elements in the disassembly. A sub-class can override these to - // customize the disassembly output. - - // Prints the name of a register. - // TODO: This currently doesn't allow renaming of V registers. - virtual void AppendRegisterNameToOutput(const Instruction* instr, - const CPURegister& reg); - - // Prints a PC-relative offset. This is used for example when disassembling - // branches to immediate offsets. - virtual void AppendPCRelativeOffsetToOutput(const Instruction* instr, - int64_t offset); - - // Prints an address, in the general case. It can be code or data. This is - // used for example to print the target address of an ADR instruction. - virtual void AppendCodeRelativeAddressToOutput(const Instruction* instr, - const void* addr); - - // Prints the address of some code. - // This is used for example to print the target address of a branch to an - // immediate offset. - // A sub-class can for example override this method to lookup the address and - // print an appropriate name. - virtual void AppendCodeRelativeCodeAddressToOutput(const Instruction* instr, - const void* addr); - - // Prints the address of some data. - // This is used for example to print the source address of a load literal - // instruction. - virtual void AppendCodeRelativeDataAddressToOutput(const Instruction* instr, - const void* addr); - - // Same as the above, but for addresses that are not relative to the code - // buffer. They are currently not used by VIXL. - virtual void AppendAddressToOutput(const Instruction* instr, - const void* addr); - virtual void AppendCodeAddressToOutput(const Instruction* instr, - const void* addr); - virtual void AppendDataAddressToOutput(const Instruction* instr, - const void* addr); - - public: - // Get/Set the offset that should be added to code addresses when printing - // code-relative addresses in the AppendCodeRelativeAddressToOutput() - // helpers. - // Below is an example of how a branch immediate instruction in memory at - // address 0xb010200 would disassemble with different offsets. - // Base address | Disassembly - // 0x0 | 0xb010200: b #+0xcc (addr 0xb0102cc) - // 0x10000 | 0xb000200: b #+0xcc (addr 0xb0002cc) - // 0xb010200 | 0x0: b #+0xcc (addr 0xcc) - void MapCodeAddress(int64_t base_address, const Instruction* instr_address); - int64_t CodeRelativeAddress(const void* instr); - - private: - void Format( - const Instruction* instr, const char* mnemonic, const char* format); - void Substitute(const Instruction* instr, const char* string); - int SubstituteField(const Instruction* instr, const char* format); - int SubstituteRegisterField(const Instruction* instr, const char* format); - int SubstituteImmediateField(const Instruction* instr, const char* format); - int SubstituteLiteralField(const Instruction* instr, const char* format); - int SubstituteBitfieldImmediateField( - const Instruction* instr, const char* format); - int SubstituteShiftField(const Instruction* instr, const char* format); - int SubstituteExtendField(const Instruction* instr, const char* format); - int SubstituteConditionField(const Instruction* instr, const char* format); - int SubstitutePCRelAddressField(const Instruction* instr, const char* format); - int SubstituteBranchTargetField(const Instruction* instr, const char* format); - int SubstituteLSRegOffsetField(const Instruction* instr, const char* format); - int SubstitutePrefetchField(const Instruction* instr, const char* format); - int SubstituteBarrierField(const Instruction* instr, const char* format); - int SubstituteSysOpField(const Instruction* instr, const char* format); - int SubstituteCrField(const Instruction* instr, const char* format); - bool RdIsZROrSP(const Instruction* instr) const { - return (instr->Rd() == kZeroRegCode); - } - - bool RnIsZROrSP(const Instruction* instr) const { - return (instr->Rn() == kZeroRegCode); - } - - bool RmIsZROrSP(const Instruction* instr) const { - return (instr->Rm() == kZeroRegCode); - } - - bool RaIsZROrSP(const Instruction* instr) const { - return (instr->Ra() == kZeroRegCode); - } - - bool IsMovzMovnImm(unsigned reg_size, uint64_t value); - - int64_t code_address_offset() const { return code_address_offset_; } - - protected: - void ResetOutput(); - void AppendToOutput(const char* string, ...) PRINTF_CHECK(2, 3); - - void set_code_address_offset(int64_t code_address_offset) { - code_address_offset_ = code_address_offset; - } - - char* buffer_; - uint32_t buffer_pos_; - uint32_t buffer_size_; - bool own_buffer_; - - int64_t code_address_offset_; -}; - - -class PrintDisassembler: public Disassembler { - public: - explicit PrintDisassembler(FILE* stream) : stream_(stream) { } - - protected: - virtual void ProcessOutput(const Instruction* instr); - - private: - FILE *stream_; -}; -} // namespace vixl - -#endif // VIXL_A64_DISASM_A64_H diff --git a/disas/libvixl/vixl/a64/instructions-a64.cc b/disas/libvixl/vixl/a64/instructions-a64.cc deleted file mode 100644 index 33992f88a41..00000000000 --- a/disas/libvixl/vixl/a64/instructions-a64.cc +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "vixl/a64/instructions-a64.h" -#include "vixl/a64/assembler-a64.h" - -namespace vixl { - - -// Floating-point infinity values. -const float16 kFP16PositiveInfinity = 0x7c00; -const float16 kFP16NegativeInfinity = 0xfc00; -const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); -const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); -const double kFP64PositiveInfinity = - rawbits_to_double(UINT64_C(0x7ff0000000000000)); -const double kFP64NegativeInfinity = - rawbits_to_double(UINT64_C(0xfff0000000000000)); - - -// The default NaN values (for FPCR.DN=1). -const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); -const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); -const float16 kFP16DefaultNaN = 0x7e00; - - -static uint64_t RotateRight(uint64_t value, - unsigned int rotate, - unsigned int width) { - VIXL_ASSERT(width <= 64); - rotate &= 63; - return ((value & ((UINT64_C(1) << rotate) - 1)) << - (width - rotate)) | (value >> rotate); -} - - -static uint64_t RepeatBitsAcrossReg(unsigned reg_size, - uint64_t value, - unsigned width) { - VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || - (width == 32)); - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - uint64_t result = value & ((UINT64_C(1) << width) - 1); - for (unsigned i = width; i < reg_size; i *= 2) { - result |= (result << i); - } - return result; -} - - -bool Instruction::IsLoad() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) != 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreMask)); - switch (op) { - case LDRB_w: - case LDRH_w: - case LDR_w: - case LDR_x: - case LDRSB_w: - case LDRSB_x: - case LDRSH_w: - case LDRSH_x: - case LDRSW_x: - case LDR_b: - case LDR_h: - case LDR_s: - case LDR_d: - case LDR_q: return true; - default: return false; - } - } -} - - -bool Instruction::IsStore() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) == 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreMask)); - switch (op) { - case STRB_w: - case STRH_w: - case STR_w: - case STR_x: - case STR_b: - case STR_h: - case STR_s: - case STR_d: - case STR_q: return true; - default: return false; - } - } -} - - -// Logical immediates can't encode zero, so a return value of zero is used to -// indicate a failure case. Specifically, where the constraints on imm_s are -// not met. -uint64_t Instruction::ImmLogical() const { - unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; - int32_t n = BitN(); - int32_t imm_s = ImmSetBits(); - int32_t imm_r = ImmRotate(); - - // An integer is constructed from the n, imm_s and imm_r bits according to - // the following table: - // - // N imms immr size S R - // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) - // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) - // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) - // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) - // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) - // 0 11110s xxxxxr 2 UInt(s) UInt(r) - // (s bits must not be all set) - // - // A pattern is constructed of size bits, where the least significant S+1 - // bits are set. The pattern is rotated right by R, and repeated across a - // 32 or 64-bit value, depending on destination register width. - // - - if (n == 1) { - if (imm_s == 0x3f) { - return 0; - } - uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1; - return RotateRight(bits, imm_r, 64); - } else { - if ((imm_s >> 1) == 0x1f) { - return 0; - } - for (int width = 0x20; width >= 0x2; width >>= 1) { - if ((imm_s & width) == 0) { - int mask = width - 1; - if ((imm_s & mask) == mask) { - return 0; - } - uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1; - return RepeatBitsAcrossReg(reg_size, - RotateRight(bits, imm_r & mask, width), - width); - } - } - } - VIXL_UNREACHABLE(); - return 0; -} - - -uint32_t Instruction::ImmNEONabcdefgh() const { - return ImmNEONabc() << 5 | ImmNEONdefgh(); -} - - -float Instruction::Imm8ToFP32(uint32_t imm8) { - // Imm8: abcdefgh (8 bits) - // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) - // where B is b ^ 1 - uint32_t bits = imm8; - uint32_t bit7 = (bits >> 7) & 0x1; - uint32_t bit6 = (bits >> 6) & 0x1; - uint32_t bit5_to_0 = bits & 0x3f; - uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); - - return rawbits_to_float(result); -} - - -float Instruction::ImmFP32() const { - return Imm8ToFP32(ImmFP()); -} - - -double Instruction::Imm8ToFP64(uint32_t imm8) { - // Imm8: abcdefgh (8 bits) - // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 - // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) - // where B is b ^ 1 - uint32_t bits = imm8; - uint64_t bit7 = (bits >> 7) & 0x1; - uint64_t bit6 = (bits >> 6) & 0x1; - uint64_t bit5_to_0 = bits & 0x3f; - uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); - - return rawbits_to_double(result); -} - - -double Instruction::ImmFP64() const { - return Imm8ToFP64(ImmFP()); -} - - -float Instruction::ImmNEONFP32() const { - return Imm8ToFP32(ImmNEONabcdefgh()); -} - - -double Instruction::ImmNEONFP64() const { - return Imm8ToFP64(ImmNEONabcdefgh()); -} - - -unsigned CalcLSDataSize(LoadStoreOp op) { - VIXL_ASSERT((LSSize_offset + LSSize_width) == (kInstructionSize * 8)); - unsigned size = static_cast(op) >> LSSize_offset; - if ((op & LSVector_mask) != 0) { - // Vector register memory operations encode the access size in the "size" - // and "opc" fields. - if ((size == 0) && ((op & LSOpc_mask) >> LSOpc_offset) >= 2) { - size = kQRegSizeInBytesLog2; - } - } - return size; -} - - -unsigned CalcLSPairDataSize(LoadStorePairOp op) { - VIXL_STATIC_ASSERT(kXRegSizeInBytes == kDRegSizeInBytes); - VIXL_STATIC_ASSERT(kWRegSizeInBytes == kSRegSizeInBytes); - switch (op) { - case STP_q: - case LDP_q: return kQRegSizeInBytesLog2; - case STP_x: - case LDP_x: - case STP_d: - case LDP_d: return kXRegSizeInBytesLog2; - default: return kWRegSizeInBytesLog2; - } -} - - -int Instruction::ImmBranchRangeBitwidth(ImmBranchType branch_type) { - switch (branch_type) { - case UncondBranchType: - return ImmUncondBranch_width; - case CondBranchType: - return ImmCondBranch_width; - case CompareBranchType: - return ImmCmpBranch_width; - case TestBranchType: - return ImmTestBranch_width; - default: - VIXL_UNREACHABLE(); - return 0; - } -} - - -int32_t Instruction::ImmBranchForwardRange(ImmBranchType branch_type) { - int32_t encoded_max = 1 << (ImmBranchRangeBitwidth(branch_type) - 1); - return encoded_max * kInstructionSize; -} - - -bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type, - int64_t offset) { - return is_intn(ImmBranchRangeBitwidth(branch_type), offset); -} - - -const Instruction* Instruction::ImmPCOffsetTarget() const { - const Instruction * base = this; - ptrdiff_t offset; - if (IsPCRelAddressing()) { - // ADR and ADRP. - offset = ImmPCRel(); - if (Mask(PCRelAddressingMask) == ADRP) { - base = AlignDown(base, kPageSize); - offset *= kPageSize; - } else { - VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR); - } - } else { - // All PC-relative branches. - VIXL_ASSERT(BranchType() != UnknownBranchType); - // Relative branch offsets are instruction-size-aligned. - offset = ImmBranch() << kInstructionSizeLog2; - } - return base + offset; -} - - -int Instruction::ImmBranch() const { - switch (BranchType()) { - case CondBranchType: return ImmCondBranch(); - case UncondBranchType: return ImmUncondBranch(); - case CompareBranchType: return ImmCmpBranch(); - case TestBranchType: return ImmTestBranch(); - default: VIXL_UNREACHABLE(); - } - return 0; -} - - -void Instruction::SetImmPCOffsetTarget(const Instruction* target) { - if (IsPCRelAddressing()) { - SetPCRelImmTarget(target); - } else { - SetBranchImmTarget(target); - } -} - - -void Instruction::SetPCRelImmTarget(const Instruction* target) { - ptrdiff_t imm21; - if ((Mask(PCRelAddressingMask) == ADR)) { - imm21 = target - this; - } else { - VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP); - uintptr_t this_page = reinterpret_cast(this) / kPageSize; - uintptr_t target_page = reinterpret_cast(target) / kPageSize; - imm21 = target_page - this_page; - } - Instr imm = Assembler::ImmPCRelAddress(static_cast(imm21)); - - SetInstructionBits(Mask(~ImmPCRel_mask) | imm); -} - - -void Instruction::SetBranchImmTarget(const Instruction* target) { - VIXL_ASSERT(((target - this) & 3) == 0); - Instr branch_imm = 0; - uint32_t imm_mask = 0; - int offset = static_cast((target - this) >> kInstructionSizeLog2); - switch (BranchType()) { - case CondBranchType: { - branch_imm = Assembler::ImmCondBranch(offset); - imm_mask = ImmCondBranch_mask; - break; - } - case UncondBranchType: { - branch_imm = Assembler::ImmUncondBranch(offset); - imm_mask = ImmUncondBranch_mask; - break; - } - case CompareBranchType: { - branch_imm = Assembler::ImmCmpBranch(offset); - imm_mask = ImmCmpBranch_mask; - break; - } - case TestBranchType: { - branch_imm = Assembler::ImmTestBranch(offset); - imm_mask = ImmTestBranch_mask; - break; - } - default: VIXL_UNREACHABLE(); - } - SetInstructionBits(Mask(~imm_mask) | branch_imm); -} - - -void Instruction::SetImmLLiteral(const Instruction* source) { - VIXL_ASSERT(IsWordAligned(source)); - ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2; - Instr imm = Assembler::ImmLLiteral(static_cast(offset)); - Instr mask = ImmLLiteral_mask; - - SetInstructionBits(Mask(~mask) | imm); -} - - -VectorFormat VectorFormatHalfWidth(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat8H || vform == kFormat4S || vform == kFormat2D || - vform == kFormatH || vform == kFormatS || vform == kFormatD); - switch (vform) { - case kFormat8H: return kFormat8B; - case kFormat4S: return kFormat4H; - case kFormat2D: return kFormat2S; - case kFormatH: return kFormatB; - case kFormatS: return kFormatH; - case kFormatD: return kFormatS; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat VectorFormatDoubleWidth(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S || - vform == kFormatB || vform == kFormatH || vform == kFormatS); - switch (vform) { - case kFormat8B: return kFormat8H; - case kFormat4H: return kFormat4S; - case kFormat2S: return kFormat2D; - case kFormatB: return kFormatH; - case kFormatH: return kFormatS; - case kFormatS: return kFormatD; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat VectorFormatFillQ(const VectorFormat vform) { - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return kFormat16B; - case kFormatH: - case kFormat4H: - case kFormat8H: return kFormat8H; - case kFormatS: - case kFormat2S: - case kFormat4S: return kFormat4S; - case kFormatD: - case kFormat1D: - case kFormat2D: return kFormat2D; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - -VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform) { - switch (vform) { - case kFormat4H: return kFormat8B; - case kFormat8H: return kFormat16B; - case kFormat2S: return kFormat4H; - case kFormat4S: return kFormat8H; - case kFormat1D: return kFormat2S; - case kFormat2D: return kFormat4S; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - -VectorFormat VectorFormatDoubleLanes(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S); - switch (vform) { - case kFormat8B: return kFormat16B; - case kFormat4H: return kFormat8H; - case kFormat2S: return kFormat4S; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat VectorFormatHalfLanes(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat16B || vform == kFormat8H || vform == kFormat4S); - switch (vform) { - case kFormat16B: return kFormat8B; - case kFormat8H: return kFormat4H; - case kFormat4S: return kFormat2S; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat ScalarFormatFromLaneSize(int laneSize) { - switch (laneSize) { - case 8: return kFormatB; - case 16: return kFormatH; - case 32: return kFormatS; - case 64: return kFormatD; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -unsigned RegisterSizeInBitsFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: return kBRegSize; - case kFormatH: return kHRegSize; - case kFormatS: return kSRegSize; - case kFormatD: return kDRegSize; - case kFormat8B: - case kFormat4H: - case kFormat2S: - case kFormat1D: return kDRegSize; - default: return kQRegSize; - } -} - - -unsigned RegisterSizeInBytesFromFormat(VectorFormat vform) { - return RegisterSizeInBitsFromFormat(vform) / 8; -} - - -unsigned LaneSizeInBitsFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return 8; - case kFormatH: - case kFormat4H: - case kFormat8H: return 16; - case kFormatS: - case kFormat2S: - case kFormat4S: return 32; - case kFormatD: - case kFormat1D: - case kFormat2D: return 64; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -int LaneSizeInBytesFromFormat(VectorFormat vform) { - return LaneSizeInBitsFromFormat(vform) / 8; -} - - -int LaneSizeInBytesLog2FromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return 0; - case kFormatH: - case kFormat4H: - case kFormat8H: return 1; - case kFormatS: - case kFormat2S: - case kFormat4S: return 2; - case kFormatD: - case kFormat1D: - case kFormat2D: return 3; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -int LaneCountFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormat16B: return 16; - case kFormat8B: - case kFormat8H: return 8; - case kFormat4H: - case kFormat4S: return 4; - case kFormat2S: - case kFormat2D: return 2; - case kFormat1D: - case kFormatB: - case kFormatH: - case kFormatS: - case kFormatD: return 1; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -int MaxLaneCountFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return 16; - case kFormatH: - case kFormat4H: - case kFormat8H: return 8; - case kFormatS: - case kFormat2S: - case kFormat4S: return 4; - case kFormatD: - case kFormat1D: - case kFormat2D: return 2; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -// Does 'vform' indicate a vector format or a scalar format? -bool IsVectorFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormatH: - case kFormatS: - case kFormatD: return false; - default: return true; - } -} - - -int64_t MaxIntFromFormat(VectorFormat vform) { - return INT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform)); -} - - -int64_t MinIntFromFormat(VectorFormat vform) { - return INT64_MIN >> (64 - LaneSizeInBitsFromFormat(vform)); -} - - -uint64_t MaxUintFromFormat(VectorFormat vform) { - return UINT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform)); -} -} // namespace vixl - diff --git a/disas/libvixl/vixl/a64/instructions-a64.h b/disas/libvixl/vixl/a64/instructions-a64.h deleted file mode 100644 index 7e0dbae36af..00000000000 --- a/disas/libvixl/vixl/a64/instructions-a64.h +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_A64_INSTRUCTIONS_A64_H_ -#define VIXL_A64_INSTRUCTIONS_A64_H_ - -#include "vixl/globals.h" -#include "vixl/utils.h" -#include "vixl/a64/constants-a64.h" - -namespace vixl { -// ISA constants. -------------------------------------------------------------- - -typedef uint32_t Instr; -const unsigned kInstructionSize = 4; -const unsigned kInstructionSizeLog2 = 2; -const unsigned kLiteralEntrySize = 4; -const unsigned kLiteralEntrySizeLog2 = 2; -const unsigned kMaxLoadLiteralRange = 1 * MBytes; - -// This is the nominal page size (as used by the adrp instruction); the actual -// size of the memory pages allocated by the kernel is likely to differ. -const unsigned kPageSize = 4 * KBytes; -const unsigned kPageSizeLog2 = 12; - -const unsigned kBRegSize = 8; -const unsigned kBRegSizeLog2 = 3; -const unsigned kBRegSizeInBytes = kBRegSize / 8; -const unsigned kBRegSizeInBytesLog2 = kBRegSizeLog2 - 3; -const unsigned kHRegSize = 16; -const unsigned kHRegSizeLog2 = 4; -const unsigned kHRegSizeInBytes = kHRegSize / 8; -const unsigned kHRegSizeInBytesLog2 = kHRegSizeLog2 - 3; -const unsigned kWRegSize = 32; -const unsigned kWRegSizeLog2 = 5; -const unsigned kWRegSizeInBytes = kWRegSize / 8; -const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3; -const unsigned kXRegSize = 64; -const unsigned kXRegSizeLog2 = 6; -const unsigned kXRegSizeInBytes = kXRegSize / 8; -const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3; -const unsigned kSRegSize = 32; -const unsigned kSRegSizeLog2 = 5; -const unsigned kSRegSizeInBytes = kSRegSize / 8; -const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3; -const unsigned kDRegSize = 64; -const unsigned kDRegSizeLog2 = 6; -const unsigned kDRegSizeInBytes = kDRegSize / 8; -const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3; -const unsigned kQRegSize = 128; -const unsigned kQRegSizeLog2 = 7; -const unsigned kQRegSizeInBytes = kQRegSize / 8; -const unsigned kQRegSizeInBytesLog2 = kQRegSizeLog2 - 3; -const uint64_t kWRegMask = UINT64_C(0xffffffff); -const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff); -const uint64_t kSRegMask = UINT64_C(0xffffffff); -const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff); -const uint64_t kSSignMask = UINT64_C(0x80000000); -const uint64_t kDSignMask = UINT64_C(0x8000000000000000); -const uint64_t kWSignMask = UINT64_C(0x80000000); -const uint64_t kXSignMask = UINT64_C(0x8000000000000000); -const uint64_t kByteMask = UINT64_C(0xff); -const uint64_t kHalfWordMask = UINT64_C(0xffff); -const uint64_t kWordMask = UINT64_C(0xffffffff); -const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff); -const uint64_t kWMaxUInt = UINT64_C(0xffffffff); -const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff); -const int64_t kXMinInt = INT64_C(0x8000000000000000); -const int32_t kWMaxInt = INT32_C(0x7fffffff); -const int32_t kWMinInt = INT32_C(0x80000000); -const unsigned kLinkRegCode = 30; -const unsigned kZeroRegCode = 31; -const unsigned kSPRegInternalCode = 63; -const unsigned kRegCodeMask = 0x1f; - -const unsigned kAddressTagOffset = 56; -const unsigned kAddressTagWidth = 8; -const uint64_t kAddressTagMask = - ((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset; -VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000)); - -// AArch64 floating-point specifics. These match IEEE-754. -const unsigned kDoubleMantissaBits = 52; -const unsigned kDoubleExponentBits = 11; -const unsigned kFloatMantissaBits = 23; -const unsigned kFloatExponentBits = 8; -const unsigned kFloat16MantissaBits = 10; -const unsigned kFloat16ExponentBits = 5; - -// Floating-point infinity values. -extern const float16 kFP16PositiveInfinity; -extern const float16 kFP16NegativeInfinity; -extern const float kFP32PositiveInfinity; -extern const float kFP32NegativeInfinity; -extern const double kFP64PositiveInfinity; -extern const double kFP64NegativeInfinity; - -// The default NaN values (for FPCR.DN=1). -extern const float16 kFP16DefaultNaN; -extern const float kFP32DefaultNaN; -extern const double kFP64DefaultNaN; - -unsigned CalcLSDataSize(LoadStoreOp op); -unsigned CalcLSPairDataSize(LoadStorePairOp op); - -enum ImmBranchType { - UnknownBranchType = 0, - CondBranchType = 1, - UncondBranchType = 2, - CompareBranchType = 3, - TestBranchType = 4 -}; - -enum AddrMode { - Offset, - PreIndex, - PostIndex -}; - -enum FPRounding { - // The first four values are encodable directly by FPCR. - FPTieEven = 0x0, - FPPositiveInfinity = 0x1, - FPNegativeInfinity = 0x2, - FPZero = 0x3, - - // The final rounding modes are only available when explicitly specified by - // the instruction (such as with fcvta). It cannot be set in FPCR. - FPTieAway, - FPRoundOdd -}; - -enum Reg31Mode { - Reg31IsStackPointer, - Reg31IsZeroRegister -}; - -// Instructions. --------------------------------------------------------------- - -class Instruction { - public: - Instr InstructionBits() const { - return *(reinterpret_cast(this)); - } - - void SetInstructionBits(Instr new_instr) { - *(reinterpret_cast(this)) = new_instr; - } - - int Bit(int pos) const { - return (InstructionBits() >> pos) & 1; - } - - uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, InstructionBits()); - } - - int32_t SignedBits(int msb, int lsb) const { - int32_t bits = *(reinterpret_cast(this)); - return signed_bitextract_32(msb, lsb, bits); - } - - Instr Mask(uint32_t mask) const { - return InstructionBits() & mask; - } - - #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ - int32_t Name() const { return Func(HighBit, LowBit); } - INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) - #undef DEFINE_GETTER - - // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), - // formed from ImmPCRelLo and ImmPCRelHi. - int ImmPCRel() const { - int offset = - static_cast((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); - int width = ImmPCRelLo_width + ImmPCRelHi_width; - return signed_bitextract_32(width - 1, 0, offset); - } - - uint64_t ImmLogical() const; - unsigned ImmNEONabcdefgh() const; - float ImmFP32() const; - double ImmFP64() const; - float ImmNEONFP32() const; - double ImmNEONFP64() const; - - unsigned SizeLS() const { - return CalcLSDataSize(static_cast(Mask(LoadStoreMask))); - } - - unsigned SizeLSPair() const { - return CalcLSPairDataSize( - static_cast(Mask(LoadStorePairMask))); - } - - int NEONLSIndex(int access_size_shift) const { - int64_t q = NEONQ(); - int64_t s = NEONS(); - int64_t size = NEONLSSize(); - int64_t index = (q << 3) | (s << 2) | size; - return static_cast(index >> access_size_shift); - } - - // Helpers. - bool IsCondBranchImm() const { - return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; - } - - bool IsUncondBranchImm() const { - return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; - } - - bool IsCompareBranch() const { - return Mask(CompareBranchFMask) == CompareBranchFixed; - } - - bool IsTestBranch() const { - return Mask(TestBranchFMask) == TestBranchFixed; - } - - bool IsImmBranch() const { - return BranchType() != UnknownBranchType; - } - - bool IsPCRelAddressing() const { - return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; - } - - bool IsLogicalImmediate() const { - return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; - } - - bool IsAddSubImmediate() const { - return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; - } - - bool IsAddSubExtended() const { - return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; - } - - bool IsLoadOrStore() const { - return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; - } - - bool IsLoad() const; - bool IsStore() const; - - bool IsLoadLiteral() const { - // This includes PRFM_lit. - return Mask(LoadLiteralFMask) == LoadLiteralFixed; - } - - bool IsMovn() const { - return (Mask(MoveWideImmediateMask) == MOVN_x) || - (Mask(MoveWideImmediateMask) == MOVN_w); - } - - static int ImmBranchRangeBitwidth(ImmBranchType branch_type); - static int32_t ImmBranchForwardRange(ImmBranchType branch_type); - static bool IsValidImmPCOffset(ImmBranchType branch_type, int64_t offset); - - // Indicate whether Rd can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rd field. - Reg31Mode RdMode() const { - // The following instructions use sp or wsp as Rd: - // Add/sub (immediate) when not setting the flags. - // Add/sub (extended) when not setting the flags. - // Logical (immediate) when not setting the flags. - // Otherwise, r31 is the zero register. - if (IsAddSubImmediate() || IsAddSubExtended()) { - if (Mask(AddSubSetFlagsBit)) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - if (IsLogicalImmediate()) { - // Of the logical (immediate) instructions, only ANDS (and its aliases) - // can set the flags. The others can all write into sp. - // Note that some logical operations are not available to - // immediate-operand instructions, so we have to combine two masks here. - if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - return Reg31IsZeroRegister; - } - - // Indicate whether Rn can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rn field. - Reg31Mode RnMode() const { - // The following instructions use sp or wsp as Rn: - // All loads and stores. - // Add/sub (immediate). - // Add/sub (extended). - // Otherwise, r31 is the zero register. - if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { - return Reg31IsStackPointer; - } - return Reg31IsZeroRegister; - } - - ImmBranchType BranchType() const { - if (IsCondBranchImm()) { - return CondBranchType; - } else if (IsUncondBranchImm()) { - return UncondBranchType; - } else if (IsCompareBranch()) { - return CompareBranchType; - } else if (IsTestBranch()) { - return TestBranchType; - } else { - return UnknownBranchType; - } - } - - // Find the target of this instruction. 'this' may be a branch or a - // PC-relative addressing instruction. - const Instruction* ImmPCOffsetTarget() const; - - // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or - // a PC-relative addressing instruction. - void SetImmPCOffsetTarget(const Instruction* target); - // Patch a literal load instruction to load from 'source'. - void SetImmLLiteral(const Instruction* source); - - // The range of a load literal instruction, expressed as 'instr +- range'. - // The range is actually the 'positive' range; the branch instruction can - // target [instr - range - kInstructionSize, instr + range]. - static const int kLoadLiteralImmBitwidth = 19; - static const int kLoadLiteralRange = - (1 << kLoadLiteralImmBitwidth) / 2 - kInstructionSize; - - // Calculate the address of a literal referred to by a load-literal - // instruction, and return it as the specified type. - // - // The literal itself is safely mutable only if the backing buffer is safely - // mutable. - template - T LiteralAddress() const { - uint64_t base_raw = reinterpret_cast(this); - int64_t offset = ImmLLiteral() << kLiteralEntrySizeLog2; - uint64_t address_raw = base_raw + offset; - - // Cast the address using a C-style cast. A reinterpret_cast would be - // appropriate, but it can't cast one integral type to another. - T address = (T)(address_raw); - - // Assert that the address can be represented by the specified type. - VIXL_ASSERT((uint64_t)(address) == address_raw); - - return address; - } - - uint32_t Literal32() const { - uint32_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - return literal; - } - - uint64_t Literal64() const { - uint64_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - return literal; - } - - float LiteralFP32() const { - return rawbits_to_float(Literal32()); - } - - double LiteralFP64() const { - return rawbits_to_double(Literal64()); - } - - const Instruction* NextInstruction() const { - return this + kInstructionSize; - } - - const Instruction* InstructionAtOffset(int64_t offset) const { - VIXL_ASSERT(IsWordAligned(this + offset)); - return this + offset; - } - - template static Instruction* Cast(T src) { - return reinterpret_cast(src); - } - - template static const Instruction* CastConst(T src) { - return reinterpret_cast(src); - } - - private: - int ImmBranch() const; - - static float Imm8ToFP32(uint32_t imm8); - static double Imm8ToFP64(uint32_t imm8); - - void SetPCRelImmTarget(const Instruction* target); - void SetBranchImmTarget(const Instruction* target); -}; - - -// Functions for handling NEON vector format information. -enum VectorFormat { - kFormatUndefined = 0xffffffff, - kFormat8B = NEON_8B, - kFormat16B = NEON_16B, - kFormat4H = NEON_4H, - kFormat8H = NEON_8H, - kFormat2S = NEON_2S, - kFormat4S = NEON_4S, - kFormat1D = NEON_1D, - kFormat2D = NEON_2D, - - // Scalar formats. We add the scalar bit to distinguish between scalar and - // vector enumerations; the bit is always set in the encoding of scalar ops - // and always clear for vector ops. Although kFormatD and kFormat1D appear - // to be the same, their meaning is subtly different. The first is a scalar - // operation, the second a vector operation that only affects one lane. - kFormatB = NEON_B | NEONScalar, - kFormatH = NEON_H | NEONScalar, - kFormatS = NEON_S | NEONScalar, - kFormatD = NEON_D | NEONScalar -}; - -VectorFormat VectorFormatHalfWidth(const VectorFormat vform); -VectorFormat VectorFormatDoubleWidth(const VectorFormat vform); -VectorFormat VectorFormatDoubleLanes(const VectorFormat vform); -VectorFormat VectorFormatHalfLanes(const VectorFormat vform); -VectorFormat ScalarFormatFromLaneSize(int lanesize); -VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform); -VectorFormat VectorFormatFillQ(const VectorFormat vform); -unsigned RegisterSizeInBitsFromFormat(VectorFormat vform); -unsigned RegisterSizeInBytesFromFormat(VectorFormat vform); -// TODO: Make the return types of these functions consistent. -unsigned LaneSizeInBitsFromFormat(VectorFormat vform); -int LaneSizeInBytesFromFormat(VectorFormat vform); -int LaneSizeInBytesLog2FromFormat(VectorFormat vform); -int LaneCountFromFormat(VectorFormat vform); -int MaxLaneCountFromFormat(VectorFormat vform); -bool IsVectorFormat(VectorFormat vform); -int64_t MaxIntFromFormat(VectorFormat vform); -int64_t MinIntFromFormat(VectorFormat vform); -uint64_t MaxUintFromFormat(VectorFormat vform); - - -enum NEONFormat { - NF_UNDEF = 0, - NF_8B = 1, - NF_16B = 2, - NF_4H = 3, - NF_8H = 4, - NF_2S = 5, - NF_4S = 6, - NF_1D = 7, - NF_2D = 8, - NF_B = 9, - NF_H = 10, - NF_S = 11, - NF_D = 12 -}; - -static const unsigned kNEONFormatMaxBits = 6; - -struct NEONFormatMap { - // The bit positions in the instruction to consider. - uint8_t bits[kNEONFormatMaxBits]; - - // Mapping from concatenated bits to format. - NEONFormat map[1 << kNEONFormatMaxBits]; -}; - -class NEONFormatDecoder { - public: - enum SubstitutionMode { - kPlaceholder, - kFormat - }; - - // Construct a format decoder with increasingly specific format maps for each - // subsitution. If no format map is specified, the default is the integer - // format map. - explicit NEONFormatDecoder(const Instruction* instr) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(IntegerFormatMap()); - } - NEONFormatDecoder(const Instruction* instr, - const NEONFormatMap* format) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(format); - } - NEONFormatDecoder(const Instruction* instr, - const NEONFormatMap* format0, - const NEONFormatMap* format1) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(format0, format1); - } - NEONFormatDecoder(const Instruction* instr, - const NEONFormatMap* format0, - const NEONFormatMap* format1, - const NEONFormatMap* format2) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(format0, format1, format2); - } - - // Set the format mapping for all or individual substitutions. - void SetFormatMaps(const NEONFormatMap* format0, - const NEONFormatMap* format1 = NULL, - const NEONFormatMap* format2 = NULL) { - VIXL_ASSERT(format0 != NULL); - formats_[0] = format0; - formats_[1] = (format1 == NULL) ? formats_[0] : format1; - formats_[2] = (format2 == NULL) ? formats_[1] : format2; - } - void SetFormatMap(unsigned index, const NEONFormatMap* format) { - VIXL_ASSERT(index <= (sizeof(formats_) / sizeof(formats_[0]))); - VIXL_ASSERT(format != NULL); - formats_[index] = format; - } - - // Substitute %s in the input string with the placeholder string for each - // register, ie. "'B", "'H", etc. - const char* SubstitutePlaceholders(const char* string) { - return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder); - } - - // Substitute %s in the input string with a new string based on the - // substitution mode. - const char* Substitute(const char* string, - SubstitutionMode mode0 = kFormat, - SubstitutionMode mode1 = kFormat, - SubstitutionMode mode2 = kFormat) { - snprintf(form_buffer_, sizeof(form_buffer_), string, - GetSubstitute(0, mode0), - GetSubstitute(1, mode1), - GetSubstitute(2, mode2)); - return form_buffer_; - } - - // Append a "2" to a mnemonic string based of the state of the Q bit. - const char* Mnemonic(const char* mnemonic) { - if ((instrbits_ & NEON_Q) != 0) { - snprintf(mne_buffer_, sizeof(mne_buffer_), "%s2", mnemonic); - return mne_buffer_; - } - return mnemonic; - } - - VectorFormat GetVectorFormat(int format_index = 0) { - return GetVectorFormat(formats_[format_index]); - } - - VectorFormat GetVectorFormat(const NEONFormatMap* format_map) { - static const VectorFormat vform[] = { - kFormatUndefined, - kFormat8B, kFormat16B, kFormat4H, kFormat8H, - kFormat2S, kFormat4S, kFormat1D, kFormat2D, - kFormatB, kFormatH, kFormatS, kFormatD - }; - VIXL_ASSERT(GetNEONFormat(format_map) < (sizeof(vform) / sizeof(vform[0]))); - return vform[GetNEONFormat(format_map)]; - } - - // Built in mappings for common cases. - - // The integer format map uses three bits (Q, size<1:0>) to encode the - // "standard" set of NEON integer vector formats. - static const NEONFormatMap* IntegerFormatMap() { - static const NEONFormatMap map = { - {23, 22, 30}, - {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_2D} - }; - return ↦ - } - - // The long integer format map uses two bits (size<1:0>) to encode the - // long set of NEON integer vector formats. These are used in narrow, wide - // and long operations. - static const NEONFormatMap* LongIntegerFormatMap() { - static const NEONFormatMap map = { - {23, 22}, {NF_8H, NF_4S, NF_2D} - }; - return ↦ - } - - // The FP format map uses two bits (Q, size<0>) to encode the NEON FP vector - // formats: NF_2S, NF_4S, NF_2D. - static const NEONFormatMap* FPFormatMap() { - // The FP format map assumes two bits (Q, size<0>) are used to encode the - // NEON FP vector formats: NF_2S, NF_4S, NF_2D. - static const NEONFormatMap map = { - {22, 30}, {NF_2S, NF_4S, NF_UNDEF, NF_2D} - }; - return ↦ - } - - // The load/store format map uses three bits (Q, 11, 10) to encode the - // set of NEON vector formats. - static const NEONFormatMap* LoadStoreFormatMap() { - static const NEONFormatMap map = { - {11, 10, 30}, - {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D} - }; - return ↦ - } - - // The logical format map uses one bit (Q) to encode the NEON vector format: - // NF_8B, NF_16B. - static const NEONFormatMap* LogicalFormatMap() { - static const NEONFormatMap map = { - {30}, {NF_8B, NF_16B} - }; - return ↦ - } - - // The triangular format map uses between two and five bits to encode the NEON - // vector format: - // xxx10->8B, xxx11->16B, xx100->4H, xx101->8H - // x1000->2S, x1001->4S, 10001->2D, all others undefined. - static const NEONFormatMap* TriangularFormatMap() { - static const NEONFormatMap map = { - {19, 18, 17, 16, 30}, - {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, - NF_4S, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_UNDEF, NF_2D, - NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, NF_4S, NF_8B, NF_16B, - NF_4H, NF_8H, NF_8B, NF_16B} - }; - return ↦ - } - - // The scalar format map uses two bits (size<1:0>) to encode the NEON scalar - // formats: NF_B, NF_H, NF_S, NF_D. - static const NEONFormatMap* ScalarFormatMap() { - static const NEONFormatMap map = { - {23, 22}, {NF_B, NF_H, NF_S, NF_D} - }; - return ↦ - } - - // The long scalar format map uses two bits (size<1:0>) to encode the longer - // NEON scalar formats: NF_H, NF_S, NF_D. - static const NEONFormatMap* LongScalarFormatMap() { - static const NEONFormatMap map = { - {23, 22}, {NF_H, NF_S, NF_D} - }; - return ↦ - } - - // The FP scalar format map assumes one bit (size<0>) is used to encode the - // NEON FP scalar formats: NF_S, NF_D. - static const NEONFormatMap* FPScalarFormatMap() { - static const NEONFormatMap map = { - {22}, {NF_S, NF_D} - }; - return ↦ - } - - // The triangular scalar format map uses between one and four bits to encode - // the NEON FP scalar formats: - // xxx1->B, xx10->H, x100->S, 1000->D, all others undefined. - static const NEONFormatMap* TriangularScalarFormatMap() { - static const NEONFormatMap map = { - {19, 18, 17, 16}, - {NF_UNDEF, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B, - NF_D, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B} - }; - return ↦ - } - - private: - // Get a pointer to a string that represents the format or placeholder for - // the specified substitution index, based on the format map and instruction. - const char* GetSubstitute(int index, SubstitutionMode mode) { - if (mode == kFormat) { - return NEONFormatAsString(GetNEONFormat(formats_[index])); - } - VIXL_ASSERT(mode == kPlaceholder); - return NEONFormatAsPlaceholder(GetNEONFormat(formats_[index])); - } - - // Get the NEONFormat enumerated value for bits obtained from the - // instruction based on the specified format mapping. - NEONFormat GetNEONFormat(const NEONFormatMap* format_map) { - return format_map->map[PickBits(format_map->bits)]; - } - - // Convert a NEONFormat into a string. - static const char* NEONFormatAsString(NEONFormat format) { - static const char* formats[] = { - "undefined", - "8b", "16b", "4h", "8h", "2s", "4s", "1d", "2d", - "b", "h", "s", "d" - }; - VIXL_ASSERT(format < (sizeof(formats) / sizeof(formats[0]))); - return formats[format]; - } - - // Convert a NEONFormat into a register placeholder string. - static const char* NEONFormatAsPlaceholder(NEONFormat format) { - VIXL_ASSERT((format == NF_B) || (format == NF_H) || - (format == NF_S) || (format == NF_D) || - (format == NF_UNDEF)); - static const char* formats[] = { - "undefined", - "undefined", "undefined", "undefined", "undefined", - "undefined", "undefined", "undefined", "undefined", - "'B", "'H", "'S", "'D" - }; - return formats[format]; - } - - // Select bits from instrbits_ defined by the bits array, concatenate them, - // and return the value. - uint8_t PickBits(const uint8_t bits[]) { - uint8_t result = 0; - for (unsigned b = 0; b < kNEONFormatMaxBits; b++) { - if (bits[b] == 0) break; - result <<= 1; - result |= ((instrbits_ & (1 << bits[b])) == 0) ? 0 : 1; - } - return result; - } - - Instr instrbits_; - const NEONFormatMap* formats_[3]; - char form_buffer_[64]; - char mne_buffer_[16]; -}; -} // namespace vixl - -#endif // VIXL_A64_INSTRUCTIONS_A64_H_ diff --git a/disas/libvixl/vixl/code-buffer.h b/disas/libvixl/vixl/code-buffer.h deleted file mode 100644 index b95babbdee2..00000000000 --- a/disas/libvixl/vixl/code-buffer.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_CODE_BUFFER_H -#define VIXL_CODE_BUFFER_H - -#include -#include "vixl/globals.h" - -namespace vixl { - -class CodeBuffer { - public: - explicit CodeBuffer(size_t capacity = 4 * KBytes); - CodeBuffer(void* buffer, size_t capacity); - ~CodeBuffer(); - - void Reset(); - - ptrdiff_t OffsetFrom(ptrdiff_t offset) const { - ptrdiff_t cursor_offset = cursor_ - buffer_; - VIXL_ASSERT((offset >= 0) && (offset <= cursor_offset)); - return cursor_offset - offset; - } - - ptrdiff_t CursorOffset() const { - return OffsetFrom(0); - } - - template - T GetOffsetAddress(ptrdiff_t offset) const { - VIXL_ASSERT((offset >= 0) && (offset <= (cursor_ - buffer_))); - return reinterpret_cast(buffer_ + offset); - } - - size_t RemainingBytes() const { - VIXL_ASSERT((cursor_ >= buffer_) && (cursor_ <= (buffer_ + capacity_))); - return (buffer_ + capacity_) - cursor_; - } - - // A code buffer can emit: - // * 32-bit data: instruction and constant. - // * 64-bit data: constant. - // * string: debug info. - void Emit32(uint32_t data) { Emit(data); } - - void Emit64(uint64_t data) { Emit(data); } - - void EmitString(const char* string); - - // Align to kInstructionSize. - void Align(); - - size_t capacity() const { return capacity_; } - - bool IsManaged() const { return managed_; } - - void Grow(size_t new_capacity); - - bool IsDirty() const { return dirty_; } - - void SetClean() { dirty_ = false; } - - private: - template - void Emit(T value) { - VIXL_ASSERT(RemainingBytes() >= sizeof(value)); - dirty_ = true; - memcpy(cursor_, &value, sizeof(value)); - cursor_ += sizeof(value); - } - - // Backing store of the buffer. - byte* buffer_; - // If true the backing store is allocated and deallocated by the buffer. The - // backing store can then grow on demand. If false the backing store is - // provided by the user and cannot be resized internally. - bool managed_; - // Pointer to the next location to be written. - byte* cursor_; - // True if there has been any write since the buffer was created or cleaned. - bool dirty_; - // Capacity in bytes of the backing store. - size_t capacity_; -}; - -} // namespace vixl - -#endif // VIXL_CODE_BUFFER_H - diff --git a/disas/libvixl/vixl/compiler-intrinsics.cc b/disas/libvixl/vixl/compiler-intrinsics.cc deleted file mode 100644 index fd551faeb1f..00000000000 --- a/disas/libvixl/vixl/compiler-intrinsics.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "compiler-intrinsics.h" - -namespace vixl { - - -int CountLeadingSignBitsFallBack(int64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - if (value >= 0) { - return CountLeadingZeros(value, width) - 1; - } else { - return CountLeadingZeros(~value, width) - 1; - } -} - - -int CountLeadingZerosFallBack(uint64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - if (value == 0) { - return width; - } - int count = 0; - value = value << (64 - width); - if ((value & UINT64_C(0xffffffff00000000)) == 0) { - count += 32; - value = value << 32; - } - if ((value & UINT64_C(0xffff000000000000)) == 0) { - count += 16; - value = value << 16; - } - if ((value & UINT64_C(0xff00000000000000)) == 0) { - count += 8; - value = value << 8; - } - if ((value & UINT64_C(0xf000000000000000)) == 0) { - count += 4; - value = value << 4; - } - if ((value & UINT64_C(0xc000000000000000)) == 0) { - count += 2; - value = value << 2; - } - if ((value & UINT64_C(0x8000000000000000)) == 0) { - count += 1; - } - count += (value == 0); - return count; -} - - -int CountSetBitsFallBack(uint64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - - // Mask out unused bits to ensure that they are not counted. - value &= (UINT64_C(0xffffffffffffffff) >> (64 - width)); - - // Add up the set bits. - // The algorithm works by adding pairs of bit fields together iteratively, - // where the size of each bit field doubles each time. - // An example for an 8-bit value: - // Bits: h g f e d c b a - // \ | \ | \ | \ | - // value = h+g f+e d+c b+a - // \ | \ | - // value = h+g+f+e d+c+b+a - // \ | - // value = h+g+f+e+d+c+b+a - const uint64_t kMasks[] = { - UINT64_C(0x5555555555555555), - UINT64_C(0x3333333333333333), - UINT64_C(0x0f0f0f0f0f0f0f0f), - UINT64_C(0x00ff00ff00ff00ff), - UINT64_C(0x0000ffff0000ffff), - UINT64_C(0x00000000ffffffff), - }; - - for (unsigned i = 0; i < (sizeof(kMasks) / sizeof(kMasks[0])); i++) { - int shift = 1 << i; - value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]); - } - - return static_cast(value); -} - - -int CountTrailingZerosFallBack(uint64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - int count = 0; - value = value << (64 - width); - if ((value & UINT64_C(0xffffffff)) == 0) { - count += 32; - value = value >> 32; - } - if ((value & 0xffff) == 0) { - count += 16; - value = value >> 16; - } - if ((value & 0xff) == 0) { - count += 8; - value = value >> 8; - } - if ((value & 0xf) == 0) { - count += 4; - value = value >> 4; - } - if ((value & 0x3) == 0) { - count += 2; - value = value >> 2; - } - if ((value & 0x1) == 0) { - count += 1; - } - count += (value == 0); - return count - (64 - width); -} - - -} // namespace vixl diff --git a/disas/libvixl/vixl/compiler-intrinsics.h b/disas/libvixl/vixl/compiler-intrinsics.h deleted file mode 100644 index 9431beddb9d..00000000000 --- a/disas/libvixl/vixl/compiler-intrinsics.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#ifndef VIXL_COMPILER_INTRINSICS_H -#define VIXL_COMPILER_INTRINSICS_H - -#include "globals.h" - -namespace vixl { - -// Helper to check whether the version of GCC used is greater than the specified -// requirement. -#define MAJOR 1000000 -#define MINOR 1000 -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) -#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \ - ((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR + __GNUC_PATCHLEVEL__) >= \ - ((major) * MAJOR + (minor) * MINOR + (patchlevel))) -#elif defined(__GNUC__) && defined(__GNUC_MINOR__) -#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \ - ((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR) >= \ - ((major) * MAJOR + (minor) * MINOR + (patchlevel))) -#else -#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) 0 -#endif - - -#if defined(__clang__) && !defined(VIXL_NO_COMPILER_BUILTINS) - -#define COMPILER_HAS_BUILTIN_CLRSB (__has_builtin(__builtin_clrsb)) -#define COMPILER_HAS_BUILTIN_CLZ (__has_builtin(__builtin_clz)) -#define COMPILER_HAS_BUILTIN_CTZ (__has_builtin(__builtin_ctz)) -#define COMPILER_HAS_BUILTIN_FFS (__has_builtin(__builtin_ffs)) -#define COMPILER_HAS_BUILTIN_POPCOUNT (__has_builtin(__builtin_popcount)) - -#elif defined(__GNUC__) && !defined(VIXL_NO_COMPILER_BUILTINS) -// The documentation for these builtins is available at: -// https://gcc.gnu.org/onlinedocs/gcc-$MAJOR.$MINOR.$PATCHLEVEL/gcc//Other-Builtins.html - -# define COMPILER_HAS_BUILTIN_CLRSB (GCC_VERSION_OR_NEWER(4, 7, 0)) -# define COMPILER_HAS_BUILTIN_CLZ (GCC_VERSION_OR_NEWER(3, 4, 0)) -# define COMPILER_HAS_BUILTIN_CTZ (GCC_VERSION_OR_NEWER(3, 4, 0)) -# define COMPILER_HAS_BUILTIN_FFS (GCC_VERSION_OR_NEWER(3, 4, 0)) -# define COMPILER_HAS_BUILTIN_POPCOUNT (GCC_VERSION_OR_NEWER(3, 4, 0)) - -#else -// One can define VIXL_NO_COMPILER_BUILTINS to force using the manually -// implemented C++ methods. - -#define COMPILER_HAS_BUILTIN_BSWAP false -#define COMPILER_HAS_BUILTIN_CLRSB false -#define COMPILER_HAS_BUILTIN_CLZ false -#define COMPILER_HAS_BUILTIN_CTZ false -#define COMPILER_HAS_BUILTIN_FFS false -#define COMPILER_HAS_BUILTIN_POPCOUNT false - -#endif - - -template -inline bool IsPowerOf2(V value) { - return (value != 0) && ((value & (value - 1)) == 0); -} - - -// Declaration of fallback functions. -int CountLeadingSignBitsFallBack(int64_t value, int width); -int CountLeadingZerosFallBack(uint64_t value, int width); -int CountSetBitsFallBack(uint64_t value, int width); -int CountTrailingZerosFallBack(uint64_t value, int width); - - -// Implementation of intrinsics functions. -// TODO: The implementations could be improved for sizes different from 32bit -// and 64bit: we could mask the values and call the appropriate builtin. - -template -inline int CountLeadingSignBits(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_CLRSB - if (width == 32) { - return __builtin_clrsb(value); - } else if (width == 64) { - return __builtin_clrsbll(value); - } -#endif - return CountLeadingSignBitsFallBack(value, width); -} - - -template -inline int CountLeadingZeros(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_CLZ - if (width == 32) { - return (value == 0) ? 32 : __builtin_clz(static_cast(value)); - } else if (width == 64) { - return (value == 0) ? 64 : __builtin_clzll(value); - } -#endif - return CountLeadingZerosFallBack(value, width); -} - - -template -inline int CountSetBits(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_POPCOUNT - if (width == 32) { - return __builtin_popcount(static_cast(value)); - } else if (width == 64) { - return __builtin_popcountll(value); - } -#endif - return CountSetBitsFallBack(value, width); -} - - -template -inline int CountTrailingZeros(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_CTZ - if (width == 32) { - return (value == 0) ? 32 : __builtin_ctz(static_cast(value)); - } else if (width == 64) { - return (value == 0) ? 64 : __builtin_ctzll(value); - } -#endif - return CountTrailingZerosFallBack(value, width); -} - -} // namespace vixl - -#endif // VIXL_COMPILER_INTRINSICS_H - diff --git a/disas/libvixl/vixl/globals.h b/disas/libvixl/vixl/globals.h deleted file mode 100644 index 3a71942f1e5..00000000000 --- a/disas/libvixl/vixl/globals.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_GLOBALS_H -#define VIXL_GLOBALS_H - -// Get standard C99 macros for integer types. -#ifndef __STDC_CONSTANT_MACROS -#define __STDC_CONSTANT_MACROS -#endif - -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -extern "C" { -#include -#include -} - -#include -#include -#include -#include -#include - -#include "vixl/platform.h" - - -typedef uint8_t byte; - -// Type for half-precision (16 bit) floating point numbers. -typedef uint16_t float16; - -const int KBytes = 1024; -const int MBytes = 1024 * KBytes; - -#define VIXL_ABORT() \ - do { printf("in %s, line %i", __FILE__, __LINE__); abort(); } while (false) -#ifdef VIXL_DEBUG - #define VIXL_ASSERT(condition) assert(condition) - #define VIXL_CHECK(condition) VIXL_ASSERT(condition) - #define VIXL_UNIMPLEMENTED() \ - do { fprintf(stderr, "UNIMPLEMENTED\t"); VIXL_ABORT(); } while (false) - #define VIXL_UNREACHABLE() \ - do { fprintf(stderr, "UNREACHABLE\t"); VIXL_ABORT(); } while (false) -#else - #define VIXL_ASSERT(condition) ((void) 0) - #define VIXL_CHECK(condition) assert(condition) - #define VIXL_UNIMPLEMENTED() ((void) 0) - #define VIXL_UNREACHABLE() ((void) 0) -#endif -// This is not as powerful as template based assertions, but it is simple. -// It assumes that the descriptions are unique. If this starts being a problem, -// we can switch to a different implemention. -#define VIXL_CONCAT(a, b) a##b -#define VIXL_STATIC_ASSERT_LINE(line, condition) \ - typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \ - __attribute__((unused)) -#define VIXL_STATIC_ASSERT(condition) \ - VIXL_STATIC_ASSERT_LINE(__LINE__, condition) - -template -inline void USE(T1) {} - -template -inline void USE(T1, T2) {} - -template -inline void USE(T1, T2, T3) {} - -template -inline void USE(T1, T2, T3, T4) {} - -#define VIXL_ALIGNMENT_EXCEPTION() \ - do { fprintf(stderr, "ALIGNMENT EXCEPTION\t"); VIXL_ABORT(); } while (0) - -// The clang::fallthrough attribute is used along with the Wimplicit-fallthrough -// argument to annotate intentional fall-through between switch labels. -// For more information please refer to: -// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough -#ifndef __has_warning - #define __has_warning(x) 0 -#endif - -// Fallthrough annotation for Clang and C++11(201103L). -#if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L - #define VIXL_FALLTHROUGH() [[clang::fallthrough]] //NOLINT -// Fallthrough annotation for GCC >= 7. -#elif __GNUC__ >= 7 - #define VIXL_FALLTHROUGH() __attribute__((fallthrough)) -#else - #define VIXL_FALLTHROUGH() do {} while (0) -#endif - -#if __cplusplus >= 201103L - #define VIXL_NO_RETURN [[noreturn]] //NOLINT -#else - #define VIXL_NO_RETURN __attribute__((noreturn)) -#endif - -// Some functions might only be marked as "noreturn" for the DEBUG build. This -// macro should be used for such cases (for more details see what -// VIXL_UNREACHABLE expands to). -#ifdef VIXL_DEBUG - #define VIXL_DEBUG_NO_RETURN VIXL_NO_RETURN -#else - #define VIXL_DEBUG_NO_RETURN -#endif - -#ifdef VIXL_INCLUDE_SIMULATOR -#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE - #define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 1 -#endif -#else -#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE - #define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 0 -#endif -#if VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE - #warning "Generating Simulator instructions without Simulator support." -#endif -#endif - -#ifdef USE_SIMULATOR - #error "Please see the release notes for USE_SIMULATOR." -#endif - -#endif // VIXL_GLOBALS_H diff --git a/disas/libvixl/vixl/invalset.h b/disas/libvixl/vixl/invalset.h deleted file mode 100644 index 2e0871f8c3f..00000000000 --- a/disas/libvixl/vixl/invalset.h +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_INVALSET_H_ -#define VIXL_INVALSET_H_ - -#include - -#include -#include - -#include "vixl/globals.h" - -namespace vixl { - -// We define a custom data structure template and its iterator as `std` -// containers do not fit the performance requirements for some of our use cases. -// -// The structure behaves like an iterable unordered set with special properties -// and restrictions. "InvalSet" stands for "Invalidatable Set". -// -// Restrictions and requirements: -// - Adding an element already present in the set is illegal. In debug mode, -// this is checked at insertion time. -// - The templated class `ElementType` must provide comparison operators so that -// `std::sort()` can be used. -// - A key must be available to represent invalid elements. -// - Elements with an invalid key must compare higher or equal to any other -// element. -// -// Use cases and performance considerations: -// Our use cases present two specificities that allow us to design this -// structure to provide fast insertion *and* fast search and deletion -// operations: -// - Elements are (generally) inserted in order (sorted according to their key). -// - A key is available to mark elements as invalid (deleted). -// The backing `std::vector` allows for fast insertions. When -// searching for an element we ensure the elements are sorted (this is generally -// the case) and perform a binary search. When deleting an element we do not -// free the associated memory immediately. Instead, an element to be deleted is -// marked with the 'invalid' key. Other methods of the container take care of -// ignoring entries marked as invalid. -// To avoid the overhead of the `std::vector` container when only few entries -// are used, a number of elements are preallocated. - -// 'ElementType' and 'KeyType' are respectively the types of the elements and -// their key. The structure only reclaims memory when safe to do so, if the -// number of elements that can be reclaimed is greater than `RECLAIM_FROM` and -// greater than ` / RECLAIM_FACTOR. -#define TEMPLATE_INVALSET_P_DECL \ - class ElementType, \ - unsigned N_PREALLOCATED_ELEMENTS, \ - class KeyType, \ - KeyType INVALID_KEY, \ - size_t RECLAIM_FROM, \ - unsigned RECLAIM_FACTOR - -#define TEMPLATE_INVALSET_P_DEF \ -ElementType, N_PREALLOCATED_ELEMENTS, \ -KeyType, INVALID_KEY, RECLAIM_FROM, RECLAIM_FACTOR - -template class InvalSetIterator; // Forward declaration. - -template class InvalSet { - public: - InvalSet(); - ~InvalSet(); - - static const size_t kNPreallocatedElements = N_PREALLOCATED_ELEMENTS; - static const KeyType kInvalidKey = INVALID_KEY; - - // It is illegal to insert an element already present in the set. - void insert(const ElementType& element); - - // Looks for the specified element in the set and - if found - deletes it. - void erase(const ElementType& element); - - // This indicates the number of (valid) elements stored in this set. - size_t size() const; - - // Returns true if no elements are stored in the set. - // Note that this does not mean the the backing storage is empty: it can still - // contain invalid elements. - bool empty() const; - - void clear(); - - const ElementType min_element(); - - // This returns the key of the minimum element in the set. - KeyType min_element_key(); - - static bool IsValid(const ElementType& element); - static KeyType Key(const ElementType& element); - static void SetKey(ElementType* element, KeyType key); - - protected: - // Returns a pointer to the element in vector_ if it was found, or NULL - // otherwise. - ElementType* Search(const ElementType& element); - - // The argument *must* point to an element stored in *this* set. - // This function is not allowed to move elements in the backing vector - // storage. - void EraseInternal(ElementType* element); - - // The elements in the range searched must be sorted. - ElementType* BinarySearch(const ElementType& element, - ElementType* start, - ElementType* end) const; - - // Sort the elements. - enum SortType { - // The 'hard' version guarantees that invalid elements are moved to the end - // of the container. - kHardSort, - // The 'soft' version only guarantees that the elements will be sorted. - // Invalid elements may still be present anywhere in the set. - kSoftSort - }; - void Sort(SortType sort_type); - - // Delete the elements that have an invalid key. The complexity is linear - // with the size of the vector. - void Clean(); - - const ElementType Front() const; - const ElementType Back() const; - - // Delete invalid trailing elements and return the last valid element in the - // set. - const ElementType CleanBack(); - - // Returns a pointer to the start or end of the backing storage. - const ElementType* StorageBegin() const; - const ElementType* StorageEnd() const; - ElementType* StorageBegin(); - ElementType* StorageEnd(); - - // Returns the index of the element within the backing storage. The element - // must belong to the backing storage. - size_t ElementIndex(const ElementType* element) const; - - // Returns the element at the specified index in the backing storage. - const ElementType* ElementAt(size_t index) const; - ElementType* ElementAt(size_t index); - - static const ElementType* FirstValidElement(const ElementType* from, - const ElementType* end); - - void CacheMinElement(); - const ElementType CachedMinElement() const; - - bool ShouldReclaimMemory() const; - void ReclaimMemory(); - - bool IsUsingVector() const { return vector_ != NULL; } - void set_sorted(bool sorted) { sorted_ = sorted; } - - // We cache some data commonly required by users to improve performance. - // We cannot cache pointers to elements as we do not control the backing - // storage. - bool valid_cached_min_; - size_t cached_min_index_; // Valid iff `valid_cached_min_` is true. - KeyType cached_min_key_; // Valid iff `valid_cached_min_` is true. - - // Indicates whether the elements are sorted. - bool sorted_; - - // This represents the number of (valid) elements in this set. - size_t size_; - - // The backing storage is either the array of preallocated elements or the - // vector. The structure starts by using the preallocated elements, and - // transitions (permanently) to using the vector once more than - // kNPreallocatedElements are used. - // Elements are only invalidated when using the vector. The preallocated - // storage always only contains valid elements. - ElementType preallocated_[kNPreallocatedElements]; - std::vector* vector_; - -#ifdef VIXL_DEBUG - // Iterators acquire and release this monitor. While a set is acquired, - // certain operations are illegal to ensure that the iterator will - // correctly iterate over the elements in the set. - int monitor_; - int monitor() const { return monitor_; } - void Acquire() { monitor_++; } - void Release() { - monitor_--; - VIXL_ASSERT(monitor_ >= 0); - } -#endif - - friend class InvalSetIterator >; - typedef ElementType _ElementType; - typedef KeyType _KeyType; -}; - - -template class InvalSetIterator { - private: - // Redefine types to mirror the associated set types. - typedef typename S::_ElementType ElementType; - typedef typename S::_KeyType KeyType; - - public: - explicit InvalSetIterator(S* inval_set); - ~InvalSetIterator(); - - ElementType* Current() const; - void Advance(); - bool Done() const; - - // Mark this iterator as 'done'. - void Finish(); - - // Delete the current element and advance the iterator to point to the next - // element. - void DeleteCurrentAndAdvance(); - - static bool IsValid(const ElementType& element); - static KeyType Key(const ElementType& element); - - protected: - void MoveToValidElement(); - - // Indicates if the iterator is looking at the vector or at the preallocated - // elements. - const bool using_vector_; - // Used when looking at the preallocated elements, or in debug mode when using - // the vector to track how many times the iterator has advanced. - size_t index_; - typename std::vector::iterator iterator_; - S* inval_set_; -}; - - -template -InvalSet::InvalSet() - : valid_cached_min_(false), - sorted_(true), size_(0), vector_(NULL) { -#ifdef VIXL_DEBUG - monitor_ = 0; -#endif -} - - -template -InvalSet::~InvalSet() { - VIXL_ASSERT(monitor_ == 0); - delete vector_; -} - - -template -void InvalSet::insert(const ElementType& element) { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(IsValid(element)); - VIXL_ASSERT(Search(element) == NULL); - set_sorted(empty() || (sorted_ && (element > CleanBack()))); - if (IsUsingVector()) { - vector_->push_back(element); - } else { - if (size_ < kNPreallocatedElements) { - preallocated_[size_] = element; - } else { - // Transition to using the vector. - vector_ = new std::vector(preallocated_, - preallocated_ + size_); - vector_->push_back(element); - } - } - size_++; - - if (valid_cached_min_ && (element < min_element())) { - cached_min_index_ = IsUsingVector() ? vector_->size() - 1 : size_ - 1; - cached_min_key_ = Key(element); - valid_cached_min_ = true; - } - - if (ShouldReclaimMemory()) { - ReclaimMemory(); - } -} - - -template -void InvalSet::erase(const ElementType& element) { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(IsValid(element)); - ElementType* local_element = Search(element); - if (local_element != NULL) { - EraseInternal(local_element); - } -} - - -template -ElementType* InvalSet::Search( - const ElementType& element) { - VIXL_ASSERT(monitor() == 0); - if (empty()) { - return NULL; - } - if (ShouldReclaimMemory()) { - ReclaimMemory(); - } - if (!sorted_) { - Sort(kHardSort); - } - if (!valid_cached_min_) { - CacheMinElement(); - } - return BinarySearch(element, ElementAt(cached_min_index_), StorageEnd()); -} - - -template -size_t InvalSet::size() const { - return size_; -} - - -template -bool InvalSet::empty() const { - return size_ == 0; -} - - -template -void InvalSet::clear() { - VIXL_ASSERT(monitor() == 0); - size_ = 0; - if (IsUsingVector()) { - vector_->clear(); - } - set_sorted(true); - valid_cached_min_ = false; -} - - -template -const ElementType InvalSet::min_element() { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(!empty()); - CacheMinElement(); - return *ElementAt(cached_min_index_); -} - - -template -KeyType InvalSet::min_element_key() { - VIXL_ASSERT(monitor() == 0); - if (valid_cached_min_) { - return cached_min_key_; - } else { - return Key(min_element()); - } -} - - -template -bool InvalSet::IsValid(const ElementType& element) { - return Key(element) != kInvalidKey; -} - - -template -void InvalSet::EraseInternal(ElementType* element) { - // Note that this function must be safe even while an iterator has acquired - // this set. - VIXL_ASSERT(element != NULL); - size_t deleted_index = ElementIndex(element); - if (IsUsingVector()) { - VIXL_ASSERT((&(vector_->front()) <= element) && - (element <= &(vector_->back()))); - SetKey(element, kInvalidKey); - } else { - VIXL_ASSERT((preallocated_ <= element) && - (element < (preallocated_ + kNPreallocatedElements))); - ElementType* end = preallocated_ + kNPreallocatedElements; - size_t copy_size = sizeof(*element) * (end - element - 1); - memmove(element, element + 1, copy_size); - } - size_--; - - if (valid_cached_min_ && - (deleted_index == cached_min_index_)) { - if (sorted_ && !empty()) { - const ElementType* min = FirstValidElement(element, StorageEnd()); - cached_min_index_ = ElementIndex(min); - cached_min_key_ = Key(*min); - valid_cached_min_ = true; - } else { - valid_cached_min_ = false; - } - } -} - - -template -ElementType* InvalSet::BinarySearch( - const ElementType& element, ElementType* start, ElementType* end) const { - if (start == end) { - return NULL; - } - VIXL_ASSERT(sorted_); - VIXL_ASSERT(start < end); - VIXL_ASSERT(!empty()); - - // Perform a binary search through the elements while ignoring invalid - // elements. - ElementType* elements = start; - size_t low = 0; - size_t high = (end - start) - 1; - while (low < high) { - // Find valid bounds. - while (!IsValid(elements[low]) && (low < high)) ++low; - while (!IsValid(elements[high]) && (low < high)) --high; - VIXL_ASSERT(low <= high); - // Avoid overflow when computing the middle index. - size_t middle = low / 2 + high / 2 + (low & high & 1); - if ((middle == low) || (middle == high)) { - break; - } - while (!IsValid(elements[middle]) && (middle < high - 1)) ++middle; - while (!IsValid(elements[middle]) && (low + 1 < middle)) --middle; - if (!IsValid(elements[middle])) { - break; - } - if (elements[middle] < element) { - low = middle; - } else { - high = middle; - } - } - - if (elements[low] == element) return &elements[low]; - if (elements[high] == element) return &elements[high]; - return NULL; -} - - -template -void InvalSet::Sort(SortType sort_type) { - VIXL_ASSERT(monitor() == 0); - if (sort_type == kSoftSort) { - if (sorted_) { - return; - } - } - if (empty()) { - return; - } - - Clean(); - std::sort(StorageBegin(), StorageEnd()); - - set_sorted(true); - cached_min_index_ = 0; - cached_min_key_ = Key(Front()); - valid_cached_min_ = true; -} - - -template -void InvalSet::Clean() { - VIXL_ASSERT(monitor() == 0); - if (empty() || !IsUsingVector()) { - return; - } - // Manually iterate through the vector storage to discard invalid elements. - ElementType* start = &(vector_->front()); - ElementType* end = start + vector_->size(); - ElementType* c = start; - ElementType* first_invalid; - ElementType* first_valid; - ElementType* next_invalid; - - while (c < end && IsValid(*c)) { c++; } - first_invalid = c; - - while (c < end) { - while (c < end && !IsValid(*c)) { c++; } - first_valid = c; - while (c < end && IsValid(*c)) { c++; } - next_invalid = c; - - ptrdiff_t n_moved_elements = (next_invalid - first_valid); - memmove(first_invalid, first_valid, n_moved_elements * sizeof(*c)); - first_invalid = first_invalid + n_moved_elements; - c = next_invalid; - } - - // Delete the trailing invalid elements. - vector_->erase(vector_->begin() + (first_invalid - start), vector_->end()); - VIXL_ASSERT(vector_->size() == size_); - - if (sorted_) { - valid_cached_min_ = true; - cached_min_index_ = 0; - cached_min_key_ = Key(*ElementAt(0)); - } else { - valid_cached_min_ = false; - } -} - - -template -const ElementType InvalSet::Front() const { - VIXL_ASSERT(!empty()); - return IsUsingVector() ? vector_->front() : preallocated_[0]; -} - - -template -const ElementType InvalSet::Back() const { - VIXL_ASSERT(!empty()); - return IsUsingVector() ? vector_->back() : preallocated_[size_ - 1]; -} - - -template -const ElementType InvalSet::CleanBack() { - VIXL_ASSERT(monitor() == 0); - if (IsUsingVector()) { - // Delete the invalid trailing elements. - typename std::vector::reverse_iterator it = vector_->rbegin(); - while (!IsValid(*it)) { - it++; - } - vector_->erase(it.base(), vector_->end()); - } - return Back(); -} - - -template -const ElementType* InvalSet::StorageBegin() const { - return IsUsingVector() ? &(vector_->front()) : preallocated_; -} - - -template -const ElementType* InvalSet::StorageEnd() const { - return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_; -} - - -template -ElementType* InvalSet::StorageBegin() { - return IsUsingVector() ? &(vector_->front()) : preallocated_; -} - - -template -ElementType* InvalSet::StorageEnd() { - return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_; -} - - -template -size_t InvalSet::ElementIndex( - const ElementType* element) const { - VIXL_ASSERT((StorageBegin() <= element) && (element < StorageEnd())); - return element - StorageBegin(); -} - - -template -const ElementType* InvalSet::ElementAt( - size_t index) const { - VIXL_ASSERT( - (IsUsingVector() && (index < vector_->size())) || (index < size_)); - return StorageBegin() + index; -} - -template -ElementType* InvalSet::ElementAt(size_t index) { - VIXL_ASSERT( - (IsUsingVector() && (index < vector_->size())) || (index < size_)); - return StorageBegin() + index; -} - -template -const ElementType* InvalSet::FirstValidElement( - const ElementType* from, const ElementType* end) { - while ((from < end) && !IsValid(*from)) { - from++; - } - return from; -} - - -template -void InvalSet::CacheMinElement() { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(!empty()); - - if (valid_cached_min_) { - return; - } - - if (sorted_) { - const ElementType* min = FirstValidElement(StorageBegin(), StorageEnd()); - cached_min_index_ = ElementIndex(min); - cached_min_key_ = Key(*min); - valid_cached_min_ = true; - } else { - Sort(kHardSort); - } - VIXL_ASSERT(valid_cached_min_); -} - - -template -bool InvalSet::ShouldReclaimMemory() const { - if (!IsUsingVector()) { - return false; - } - size_t n_invalid_elements = vector_->size() - size_; - return (n_invalid_elements > RECLAIM_FROM) && - (n_invalid_elements > vector_->size() / RECLAIM_FACTOR); -} - - -template -void InvalSet::ReclaimMemory() { - VIXL_ASSERT(monitor() == 0); - Clean(); -} - - -template -InvalSetIterator::InvalSetIterator(S* inval_set) - : using_vector_((inval_set != NULL) && inval_set->IsUsingVector()), - index_(0), - inval_set_(inval_set) { - if (inval_set != NULL) { - inval_set->Sort(S::kSoftSort); -#ifdef VIXL_DEBUG - inval_set->Acquire(); -#endif - if (using_vector_) { - iterator_ = typename std::vector::iterator( - inval_set_->vector_->begin()); - } - MoveToValidElement(); - } -} - - -template -InvalSetIterator::~InvalSetIterator() { -#ifdef VIXL_DEBUG - if (inval_set_ != NULL) { - inval_set_->Release(); - } -#endif -} - - -template -typename S::_ElementType* InvalSetIterator::Current() const { - VIXL_ASSERT(!Done()); - if (using_vector_) { - return &(*iterator_); - } else { - return &(inval_set_->preallocated_[index_]); - } -} - - -template -void InvalSetIterator::Advance() { - VIXL_ASSERT(!Done()); - if (using_vector_) { - iterator_++; -#ifdef VIXL_DEBUG - index_++; -#endif - MoveToValidElement(); - } else { - index_++; - } -} - - -template -bool InvalSetIterator::Done() const { - if (using_vector_) { - bool done = (iterator_ == inval_set_->vector_->end()); - VIXL_ASSERT(done == (index_ == inval_set_->size())); - return done; - } else { - return index_ == inval_set_->size(); - } -} - - -template -void InvalSetIterator::Finish() { - VIXL_ASSERT(inval_set_->sorted_); - if (using_vector_) { - iterator_ = inval_set_->vector_->end(); - } - index_ = inval_set_->size(); -} - - -template -void InvalSetIterator::DeleteCurrentAndAdvance() { - if (using_vector_) { - inval_set_->EraseInternal(&(*iterator_)); - MoveToValidElement(); - } else { - inval_set_->EraseInternal(inval_set_->preallocated_ + index_); - } -} - - -template -bool InvalSetIterator::IsValid(const ElementType& element) { - return S::IsValid(element); -} - - -template -typename S::_KeyType InvalSetIterator::Key(const ElementType& element) { - return S::Key(element); -} - - -template -void InvalSetIterator::MoveToValidElement() { - if (using_vector_) { - while ((iterator_ != inval_set_->vector_->end()) && !IsValid(*iterator_)) { - iterator_++; - } - } else { - VIXL_ASSERT(inval_set_->empty() || IsValid(inval_set_->preallocated_[0])); - // Nothing to do. - } -} - -#undef TEMPLATE_INVALSET_P_DECL -#undef TEMPLATE_INVALSET_P_DEF - -} // namespace vixl - -#endif // VIXL_INVALSET_H_ diff --git a/disas/libvixl/vixl/platform.h b/disas/libvixl/vixl/platform.h deleted file mode 100644 index 26a74de81bb..00000000000 --- a/disas/libvixl/vixl/platform.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef PLATFORM_H -#define PLATFORM_H - -// Define platform specific functionalities. -extern "C" { -#include -} - -namespace vixl { -inline void HostBreakpoint() { raise(SIGINT); } -} // namespace vixl - -#endif diff --git a/disas/libvixl/vixl/utils.cc b/disas/libvixl/vixl/utils.cc deleted file mode 100644 index 69304d266d7..00000000000 --- a/disas/libvixl/vixl/utils.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "vixl/utils.h" -#include - -namespace vixl { - -uint32_t float_to_rawbits(float value) { - uint32_t bits = 0; - memcpy(&bits, &value, 4); - return bits; -} - - -uint64_t double_to_rawbits(double value) { - uint64_t bits = 0; - memcpy(&bits, &value, 8); - return bits; -} - - -float rawbits_to_float(uint32_t bits) { - float value = 0.0; - memcpy(&value, &bits, 4); - return value; -} - - -double rawbits_to_double(uint64_t bits) { - double value = 0.0; - memcpy(&value, &bits, 8); - return value; -} - - -uint32_t float_sign(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(31, 31, rawbits); -} - - -uint32_t float_exp(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(30, 23, rawbits); -} - - -uint32_t float_mantissa(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(22, 0, rawbits); -} - - -uint32_t double_sign(double val) { - uint64_t rawbits = double_to_rawbits(val); - return static_cast(unsigned_bitextract_64(63, 63, rawbits)); -} - - -uint32_t double_exp(double val) { - uint64_t rawbits = double_to_rawbits(val); - return static_cast(unsigned_bitextract_64(62, 52, rawbits)); -} - - -uint64_t double_mantissa(double val) { - uint64_t rawbits = double_to_rawbits(val); - return unsigned_bitextract_64(51, 0, rawbits); -} - - -float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa) { - uint32_t bits = (sign << 31) | (exp << 23) | mantissa; - return rawbits_to_float(bits); -} - - -double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa) { - uint64_t bits = (sign << 63) | (exp << 52) | mantissa; - return rawbits_to_double(bits); -} - - -int float16classify(float16 value) { - uint16_t exponent_max = (1 << 5) - 1; - uint16_t exponent_mask = exponent_max << 10; - uint16_t mantissa_mask = (1 << 10) - 1; - - uint16_t exponent = (value & exponent_mask) >> 10; - uint16_t mantissa = value & mantissa_mask; - if (exponent == 0) { - if (mantissa == 0) { - return FP_ZERO; - } - return FP_SUBNORMAL; - } else if (exponent == exponent_max) { - if (mantissa == 0) { - return FP_INFINITE; - } - return FP_NAN; - } - return FP_NORMAL; -} - - -unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { - VIXL_ASSERT((reg_size % 8) == 0); - int count = 0; - for (unsigned i = 0; i < (reg_size / 16); i++) { - if ((imm & 0xffff) == 0) { - count++; - } - imm >>= 16; - } - return count; -} - -} // namespace vixl diff --git a/disas/libvixl/vixl/utils.h b/disas/libvixl/vixl/utils.h deleted file mode 100644 index ecb0f1014ab..00000000000 --- a/disas/libvixl/vixl/utils.h +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef VIXL_UTILS_H -#define VIXL_UTILS_H - -#include -#include -#include "vixl/globals.h" -#include "vixl/compiler-intrinsics.h" - -namespace vixl { - -// Macros for compile-time format checking. -#if GCC_VERSION_OR_NEWER(4, 4, 0) -#define PRINTF_CHECK(format_index, varargs_index) \ - __attribute__((format(gnu_printf, format_index, varargs_index))) -#else -#define PRINTF_CHECK(format_index, varargs_index) -#endif - -// Check number width. -inline bool is_intn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - int64_t limit = INT64_C(1) << (n - 1); - return (-limit <= x) && (x < limit); -} - -inline bool is_uintn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - return !(x >> n); -} - -inline uint32_t truncate_to_intn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - return static_cast(x & ((INT64_C(1) << n) - 1)); -} - -#define INT_1_TO_63_LIST(V) \ -V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ -V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ -V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ -V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ -V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ -V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ -V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ -V(57) V(58) V(59) V(60) V(61) V(62) V(63) - -#define DECLARE_IS_INT_N(N) \ -inline bool is_int##N(int64_t x) { return is_intn(N, x); } -#define DECLARE_IS_UINT_N(N) \ -inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } -#define DECLARE_TRUNCATE_TO_INT_N(N) \ -inline uint32_t truncate_to_int##N(int x) { return truncate_to_intn(N, x); } -INT_1_TO_63_LIST(DECLARE_IS_INT_N) -INT_1_TO_63_LIST(DECLARE_IS_UINT_N) -INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) -#undef DECLARE_IS_INT_N -#undef DECLARE_IS_UINT_N -#undef DECLARE_TRUNCATE_TO_INT_N - -// Bit field extraction. -inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { - return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); -} - -inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { - return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); -} - -inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { - return (x << (31 - msb)) >> (lsb + 31 - msb); -} - -inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { - return (x << (63 - msb)) >> (lsb + 63 - msb); -} - -// Floating point representation. -uint32_t float_to_rawbits(float value); -uint64_t double_to_rawbits(double value); -float rawbits_to_float(uint32_t bits); -double rawbits_to_double(uint64_t bits); - -uint32_t float_sign(float val); -uint32_t float_exp(float val); -uint32_t float_mantissa(float val); -uint32_t double_sign(double val); -uint32_t double_exp(double val); -uint64_t double_mantissa(double val); - -float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa); -double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa); - -// An fpclassify() function for 16-bit half-precision floats. -int float16classify(float16 value); - -// NaN tests. -inline bool IsSignallingNaN(double num) { - const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - uint64_t raw = double_to_rawbits(num); - if (std::isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -inline bool IsSignallingNaN(float num) { - const uint32_t kFP32QuietNaNMask = 0x00400000; - uint32_t raw = float_to_rawbits(num); - if (std::isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -inline bool IsSignallingNaN(float16 num) { - const uint16_t kFP16QuietNaNMask = 0x0200; - return (float16classify(num) == FP_NAN) && - ((num & kFP16QuietNaNMask) == 0); -} - - -template -inline bool IsQuietNaN(T num) { - return std::isnan(num) && !IsSignallingNaN(num); -} - - -// Convert the NaN in 'num' to a quiet NaN. -inline double ToQuietNaN(double num) { - const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - VIXL_ASSERT(std::isnan(num)); - return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask); -} - - -inline float ToQuietNaN(float num) { - const uint32_t kFP32QuietNaNMask = 0x00400000; - VIXL_ASSERT(std::isnan(num)); - return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask); -} - - -// Fused multiply-add. -inline double FusedMultiplyAdd(double op1, double op2, double a) { - return fma(op1, op2, a); -} - - -inline float FusedMultiplyAdd(float op1, float op2, float a) { - return fmaf(op1, op2, a); -} - - -inline uint64_t LowestSetBit(uint64_t value) { - return value & -value; -} - - -template -inline int HighestSetBitPosition(T value) { - VIXL_ASSERT(value != 0); - return (sizeof(value) * 8 - 1) - CountLeadingZeros(value); -} - - -template -inline int WhichPowerOf2(V value) { - VIXL_ASSERT(IsPowerOf2(value)); - return CountTrailingZeros(value); -} - - -unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); - - -template -T ReverseBits(T value) { - VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || - (sizeof(value) == 4) || (sizeof(value) == 8)); - T result = 0; - for (unsigned i = 0; i < (sizeof(value) * 8); i++) { - result = (result << 1) | (value & 1); - value >>= 1; - } - return result; -} - - -template -T ReverseBytes(T value, int block_bytes_log2) { - VIXL_ASSERT((sizeof(value) == 4) || (sizeof(value) == 8)); - VIXL_ASSERT((1U << block_bytes_log2) <= sizeof(value)); - // Split the 64-bit value into an 8-bit array, where b[0] is the least - // significant byte, and b[7] is the most significant. - uint8_t bytes[8]; - uint64_t mask = UINT64_C(0xff00000000000000); - for (int i = 7; i >= 0; i--) { - bytes[i] = (static_cast(value) & mask) >> (i * 8); - mask >>= 8; - } - - // Permutation tables for REV instructions. - // permute_table[0] is used by REV16_x, REV16_w - // permute_table[1] is used by REV32_x, REV_w - // permute_table[2] is used by REV_x - VIXL_ASSERT((0 < block_bytes_log2) && (block_bytes_log2 < 4)); - static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1}, - {4, 5, 6, 7, 0, 1, 2, 3}, - {0, 1, 2, 3, 4, 5, 6, 7} }; - T result = 0; - for (int i = 0; i < 8; i++) { - result <<= 8; - result |= bytes[permute_table[block_bytes_log2 - 1][i]]; - } - return result; -} - - -// Pointer alignment -// TODO: rename/refactor to make it specific to instructions. -template -bool IsWordAligned(T pointer) { - VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) - return ((intptr_t)(pointer) & 3) == 0; -} - -// Increment a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignUp(T pointer, size_t alignment) { - // Use C-style casts to get static_cast behaviour for integral types (T), and - // reinterpret_cast behaviour for other types. - - uint64_t pointer_raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - - size_t align_step = (alignment - pointer_raw) % alignment; - VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); - - return (T)(pointer_raw + align_step); -} - -// Decrement a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignDown(T pointer, size_t alignment) { - // Use C-style casts to get static_cast behaviour for integral types (T), and - // reinterpret_cast behaviour for other types. - - uint64_t pointer_raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - - size_t align_step = pointer_raw % alignment; - VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); - - return (T)(pointer_raw - align_step); -} - -} // namespace vixl - -#endif // VIXL_UTILS_H diff --git a/disas/meson.build b/disas/meson.build index 7da48ea74a2..ba22f7cbcdc 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -1,9 +1,4 @@ -libvixl_ss = ss.source_set() -subdir('libvixl') - common_ss.add(when: 'CONFIG_ALPHA_DIS', if_true: files('alpha.c')) -common_ss.add(when: 'CONFIG_ARM_A64_DIS', if_true: files('arm-a64.cc')) -common_ss.add_all(when: 'CONFIG_ARM_A64_DIS', if_true: libvixl_ss) common_ss.add(when: 'CONFIG_CRIS_DIS', if_true: files('cris.c')) common_ss.add(when: 'CONFIG_HEXAGON_DIS', if_true: files('hexagon.c')) common_ss.add(when: 'CONFIG_HPPA_DIS', if_true: files('hppa.c')) diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 1958edb4305..ebde20f9815 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -88,7 +88,7 @@ Windows The project aims to support the two most recent versions of Windows that are still supported by the vendor. The minimum Windows API that is currently -targeted is "Windows 7", so theoretically the QEMU binaries can still be run +targeted is "Windows 8", so theoretically the QEMU binaries can still be run on older versions of Windows, too. However, such old versions of Windows are not tested anymore, so it is recommended to use one of the latest versions of Windows instead. diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 19a91b575fb..7ee26626d5c 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -225,6 +225,14 @@ Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. System emulator machines ------------------------ +Arm ``virt`` machine ``dtb-kaslr-seed`` property +'''''''''''''''''''''''''''''''''''''''''''''''' + +The ``dtb-kaslr-seed`` property on the ``virt`` board has been +deprecated; use the new name ``dtb-randomness`` instead. The new name +better reflects the way this property affects all random data within +the device tree blob, not just the ``kaslr-seed`` node. + PPC 405 ``taihu`` machine (since 7.0) ''''''''''''''''''''''''''''''''''''' diff --git a/docs/conf.py b/docs/conf.py index 49dab44cca5..e33cf3d3812 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -126,7 +126,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index 09a8d12c2c7..fec33ce148f 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -39,7 +39,7 @@ ideas from other posts. If you do subscribe, be prepared for a high volume of email, often over one thousand messages in a week. The list is moderated; first-time posts from an email address (whether or not you subscribed) may be subject to some delay while waiting for a moderator -to whitelist your address. +to allow your address. The larger your contribution is, or if you plan on becoming a long-term contributor, then the more important the rest of this page becomes. diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index d7cf904f7fe..3f18ab424eb 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -1376,14 +1376,6 @@ Front-end message types For further details on postcopy, see ``VHOST_USER_SET_MEM_TABLE``. They apply to ``VHOST_USER_ADD_MEM_REG`` accordingly. - Exactly one file descriptor from which the memory is mapped is - passed in the ancillary data. - - In postcopy mode (see ``VHOST_USER_POSTCOPY_LISTEN``), the back-end - replies with the bases of the memory mapped region to the front-end. - For further details on postcopy, see ``VHOST_USER_SET_MEM_TABLE``. - They apply to ``VHOST_USER_ADD_MEM_REG`` accordingly. - ``VHOST_USER_REM_MEM_REG`` :id: 38 :equivalent ioctl: N/A @@ -1408,14 +1400,6 @@ Front-end message types accept messages with one file descriptor. If a file descriptor is passed, the back-end MUST close it without using it otherwise. - The memory region to be removed is identified by its guest address, - user address and size. The mmap offset is ignored. - - No file descriptors SHOULD be passed in the ancillary data. For - compatibility with existing incorrect implementations, the back-end MAY - accept messages with one file descriptor. If a file descriptor is - passed, the back-end MUST close it without using it otherwise. - ``VHOST_USER_SET_STATUS`` :id: 39 :equivalent ioctl: VHOST_VDPA_SET_STATUS diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.rst similarity index 58% rename from docs/specs/fw_cfg.txt rename to docs/specs/fw_cfg.rst index 3e6d586f66b..5ad47a901c9 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.rst @@ -1,7 +1,9 @@ +=========================================== QEMU Firmware Configuration (fw_cfg) Device =========================================== -= Guest-side Hardware Interface = +Guest-side Hardware Interface +============================= This hardware interface allows the guest to retrieve various data items (blobs) that can influence how the firmware configures itself, or may @@ -9,7 +11,8 @@ contain tables to be installed for the guest OS. Examples include device boot order, ACPI and SMBIOS tables, virtual machine UUID, SMP and NUMA information, kernel/initrd images for direct (Linux) kernel booting, etc. -== Selector (Control) Register == +Selector (Control) Register +--------------------------- * Write only * Location: platform dependent (IOport or MMIO) @@ -30,10 +33,12 @@ of 1 means the item's data can be overwritten by writes to the data register. In other words, configuration write mode is enabled when the selector value is between 0x4000-0x7fff or 0xc000-0xffff. -NOTE: As of QEMU v2.4, writes to the fw_cfg data register are no +.. NOTE:: + As of QEMU v2.4, writes to the fw_cfg data register are no longer supported, and will be ignored (treated as no-ops)! -NOTE: As of QEMU v2.9, writes are reinstated, but only through the DMA +.. NOTE:: + As of QEMU v2.9, writes are reinstated, but only through the DMA interface (see below). Furthermore, writeability of any specific item is governed independently of Bit14 in the selector key value. @@ -45,17 +50,19 @@ items are accessed with a selector value between 0x0000-0x7fff, and architecture specific configuration items are accessed with a selector value between 0x8000-0xffff. -== Data Register == +Data Register +------------- * Read/Write (writes ignored as of QEMU v2.4, but see the DMA interface) -* Location: platform dependent (IOport [*] or MMIO) +* Location: platform dependent (IOport [#]_ or MMIO) * Width: 8-bit (if IOport), 8/16/32/64-bit (if MMIO) * Endianness: string-preserving -[*] On platforms where the data register is exposed as an IOport, its -port number will always be one greater than the port number of the -selector register. In other words, the two ports overlap, and can not -be mapped separately. +.. [#] + On platforms where the data register is exposed as an IOport, its + port number will always be one greater than the port number of the + selector register. In other words, the two ports overlap, and can not + be mapped separately. The data register allows access to an array of bytes for each firmware configuration data item. The specific item is selected by writing to @@ -74,91 +81,103 @@ An N-byte wide read of the data register will return the next available N bytes of the selected firmware configuration item, as a substring, in increasing address order, similar to memcpy(). -== Register Locations == - -=== x86, x86_64 Register Locations === +Register Locations +------------------ -Selector Register IOport: 0x510 -Data Register IOport: 0x511 -DMA Address IOport: 0x514 +x86, x86_64 + * Selector Register IOport: 0x510 + * Data Register IOport: 0x511 + * DMA Address IOport: 0x514 -=== Arm Register Locations === +Arm + * Selector Register address: Base + 8 (2 bytes) + * Data Register address: Base + 0 (8 bytes) + * DMA Address address: Base + 16 (8 bytes) -Selector Register address: Base + 8 (2 bytes) -Data Register address: Base + 0 (8 bytes) -DMA Address address: Base + 16 (8 bytes) +ACPI Interface +-------------- -== ACPI Interface == - -The fw_cfg device is defined with ACPI ID "QEMU0002". Since we expect +The fw_cfg device is defined with ACPI ID ``QEMU0002``. Since we expect ACPI tables to be passed into the guest through the fw_cfg device itself, the guest-side firmware can not use ACPI to find fw_cfg. However, once the firmware is finished setting up ACPI tables and hands control over to the guest kernel, the latter can use the fw_cfg ACPI node for a more accurate inventory of in-use IOport or MMIO regions. -== Firmware Configuration Items == +Firmware Configuration Items +---------------------------- -=== Signature (Key 0x0000, FW_CFG_SIGNATURE) === +Signature (Key 0x0000, ``FW_CFG_SIGNATURE``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The presence of the fw_cfg selector and data registers can be verified -by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), +by selecting the "signature" item using key 0x0000 (``FW_CFG_SIGNATURE``), and reading four bytes from the data register. If the fw_cfg device is -present, the four bytes read will contain the characters "QEMU". +present, the four bytes read will contain the characters ``QEMU``. If the DMA interface is available, then reading the DMA Address -Register returns 0x51454d5520434647 ("QEMU CFG" in big-endian format). +Register returns 0x51454d5520434647 (``QEMU CFG`` in big-endian format). -=== Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === +Revision / feature bitmap (Key 0x0001, ``FW_CFG_ID``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A 32-bit little-endian unsigned int, this item is used to check for enabled features. - - Bit 0: traditional interface. Always set. - - Bit 1: DMA interface. -=== File Directory (Key 0x0019, FW_CFG_FILE_DIR) === +- Bit 0: traditional interface. Always set. +- Bit 1: DMA interface. + +File Directory (Key 0x0019, ``FW_CFG_FILE_DIR``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. highlight:: c Firmware configuration items stored at selector keys 0x0020 or higher -(FW_CFG_FILE_FIRST or higher) have an associated entry in a directory +(``FW_CFG_FILE_FIRST`` or higher) have an associated entry in a directory structure, which makes it easier for guest-side firmware to identify -and retrieve them. The format of this file directory (from fw_cfg.h in -the QEMU source tree) is shown here, slightly annotated for clarity: +and retrieve them. The format of this file directory (from ``fw_cfg.h`` in +the QEMU source tree) is shown here, slightly annotated for clarity:: -struct FWCfgFiles { /* the entire file directory fw_cfg item */ - uint32_t count; /* number of entries, in big-endian format */ - struct FWCfgFile f[]; /* array of file entries, see below */ -}; + struct FWCfgFiles { /* the entire file directory fw_cfg item */ + uint32_t count; /* number of entries, in big-endian format */ + struct FWCfgFile f[]; /* array of file entries, see below */ + }; -struct FWCfgFile { /* an individual file entry, 64 bytes total */ - uint32_t size; /* size of referenced fw_cfg item, big-endian */ - uint16_t select; /* selector key of fw_cfg item, big-endian */ - uint16_t reserved; - char name[56]; /* fw_cfg item name, NUL-terminated ascii */ -}; + struct FWCfgFile { /* an individual file entry, 64 bytes total */ + uint32_t size; /* size of referenced fw_cfg item, big-endian */ + uint16_t select; /* selector key of fw_cfg item, big-endian */ + uint16_t reserved; + char name[56]; /* fw_cfg item name, NUL-terminated ascii */ + }; -=== All Other Data Items === +All Other Data Items +~~~~~~~~~~~~~~~~~~~~ Please consult the QEMU source for the most up-to-date and authoritative list of selector keys and their respective items' purpose, format and writeability. -=== Ranges === +Ranges +~~~~~~ Theoretically, there may be up to 0x4000 generic firmware configuration items, and up to 0x4000 architecturally specific ones. +=============== =========== Selector Reg. Range Usage ---------------- ----------- +=============== =========== 0x0000 - 0x3fff Generic (0x0000 - 0x3fff, generally RO, possibly RW through - the DMA interface in QEMU v2.9+) + the DMA interface in QEMU v2.9+) 0x4000 - 0x7fff Generic (0x0000 - 0x3fff, RW, ignored in QEMU v2.4+) 0x8000 - 0xbfff Arch. Specific (0x0000 - 0x3fff, generally RO, possibly RW - through the DMA interface in QEMU v2.9+) + through the DMA interface in QEMU v2.9+) 0xc000 - 0xffff Arch. Specific (0x0000 - 0x3fff, RW, ignored in v2.4+) +=============== =========== In practice, the number of allowed firmware configuration items depends on the machine type/version. -= Guest-side DMA Interface = +Guest-side DMA Interface +======================== If bit 1 of the feature bitmap is set, the DMA interface is present. This does not replace the existing fw_cfg interface, it is an add-on. This interface @@ -171,68 +190,74 @@ addresses can be triggered with just one write, whereas operations with 64-bit addresses can be triggered with one 64-bit write or two 32-bit writes, starting with the most significant half (at offset 0). -In this register, the physical address of a FWCfgDmaAccess structure in RAM -should be written. This is the format of the FWCfgDmaAccess structure: +In this register, the physical address of a ``FWCfgDmaAccess`` structure in RAM +should be written. This is the format of the ``FWCfgDmaAccess`` structure:: -typedef struct FWCfgDmaAccess { - uint32_t control; - uint32_t length; - uint64_t address; -} FWCfgDmaAccess; + typedef struct FWCfgDmaAccess { + uint32_t control; + uint32_t length; + uint64_t address; + } FWCfgDmaAccess; The fields of the structure are in big endian mode, and the field at the lowest -address is the "control" field. +address is the ``control`` field. + +The ``control`` field has the following bits: -The "control" field has the following bits: - - Bit 0: Error - - Bit 1: Read - - Bit 2: Skip - - Bit 3: Select. The upper 16 bits are the selected index. - - Bit 4: Write +- Bit 0: Error +- Bit 1: Read +- Bit 2: Skip +- Bit 3: Select. The upper 16 bits are the selected index. +- Bit 4: Write -When an operation is triggered, if the "control" field has bit 3 set, the +When an operation is triggered, if the ``control`` field has bit 3 set, the upper 16 bits are interpreted as an index of a firmware configuration item. This has the same effect as writing the selector register. -If the "control" field has bit 1 set, a read operation will be performed. -"length" bytes for the current selector and offset will be copied into the -physical RAM address specified by the "address" field. +If the ``control`` field has bit 1 set, a read operation will be performed. +``length`` bytes for the current selector and offset will be copied into the +physical RAM address specified by the ``address`` field. -If the "control" field has bit 4 set (and not bit 1), a write operation will be -performed. "length" bytes will be copied from the physical RAM address -specified by the "address" field to the current selector and offset. QEMU +If the ``control`` field has bit 4 set (and not bit 1), a write operation will be +performed. ``length`` bytes will be copied from the physical RAM address +specified by the ``address`` field to the current selector and offset. QEMU prevents starting or finishing the write beyond the end of the item associated with the current selector (i.e., the item cannot be resized). Truncated writes are dropped entirely. Writes to read-only items are also rejected. All of these -write errors set bit 0 (the error bit) in the "control" field. +write errors set bit 0 (the error bit) in the ``control`` field. -If the "control" field has bit 2 set (and neither bit 1 nor bit 4), a skip +If the ``control`` field has bit 2 set (and neither bit 1 nor bit 4), a skip operation will be performed. The offset for the current selector will be -advanced "length" bytes. +advanced ``length`` bytes. + +To check the result, read the ``control`` field: -To check the result, read the "control" field: - error bit set -> something went wrong. - all bits cleared -> transfer finished successfully. - otherwise -> transfer still in progress (doesn't happen - today due to implementation not being async, - but may in the future). +Error bit set + Something went wrong. +All bits cleared + Transfer finished successfully. +Otherwise + Transfer still in progress + (doesn't happen today due to implementation not being async, + but may in the future). -= Externally Provided Items = +Externally Provided Items +========================= Since v2.4, "file" fw_cfg items (i.e., items with selector keys above -FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file +``FW_CFG_FILE_FIRST``, and with a corresponding entry in the fw_cfg file directory structure) may be inserted via the QEMU command line, using -the following syntax: +the following syntax:: -fw_cfg [name=],file= -Or +Or:: -fw_cfg [name=],string= Since v5.1, QEMU allows some objects to generate fw_cfg-specific content, the content is then associated with a "file" item using the 'gen_id' option -in the command line, using the following syntax: +in the command line, using the following syntax:: -object ,id=,[generator-specific-options] \ -fw_cfg [name=],gen_id= @@ -241,24 +266,24 @@ See QEMU man page for more documentation. Using item_name with plain ASCII characters only is recommended. -Item names beginning with "opt/" are reserved for users. QEMU will +Item names beginning with ``opt/`` are reserved for users. QEMU will never create entries with such names unless explicitly ordered by the user. To avoid clashes among different users, it is strongly recommended -that you use names beginning with opt/RFQDN/, where RFQDN is a reverse +that you use names beginning with ``opt/RFQDN/``, where RFQDN is a reverse fully qualified domain name you control. For instance, if SeaBIOS -wanted to define additional names, the prefix "opt/org.seabios/" would +wanted to define additional names, the prefix ``opt/org.seabios/`` would be appropriate. -For historical reasons, "opt/ovmf/" is reserved for OVMF firmware. +For historical reasons, ``opt/ovmf/`` is reserved for OVMF firmware. -Prefix "opt/org.qemu/" is reserved for QEMU itself. +Prefix ``opt/org.qemu/`` is reserved for QEMU itself. -Use of names not beginning with "opt/" is potentially dangerous and +Use of names not beginning with ``opt/`` is potentially dangerous and entirely unsupported. QEMU will warn if you try. -Use of names not beginning with "opt/" is tolerated with 'gen_id' (that +Use of names not beginning with ``opt/`` is tolerated with 'gen_id' (that is, the warning is suppressed), but you must know exactly what you're doing. diff --git a/docs/specs/index.rst b/docs/specs/index.rst index e10684bf535..a58d9311cb3 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -20,3 +20,4 @@ guest hardware that is specific to QEMU. acpi_nvdimm acpi_erst sev-guest-firmware + fw_cfg diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 5d0a7865d3a..6c5b05128ea 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -31,7 +31,10 @@ AST2600 SoC based machines : - ``tacoma-bmc`` OpenPOWER Witherspoon POWER9 AST2600 BMC - ``rainier-bmc`` IBM Rainier POWER10 BMC - ``fuji-bmc`` Facebook Fuji BMC +- ``bletchley-bmc`` Facebook Bletchley BMC - ``fby35-bmc`` Facebook fby35 BMC +- ``qcom-dc-scm-v1-bmc`` Qualcomm DC-SCM V1 BMC +- ``qcom-firework-bmc`` Qualcomm Firework BMC Supported devices ----------------- @@ -40,7 +43,7 @@ Supported devices * Interrupt Controller (VIC) * Timer Controller * RTC Controller - * I2C Controller + * I2C Controller, including the new register interface of the AST2600 * System Control Unit (SCU) * SRAM mapping * X-DMA Controller (basic interface) @@ -57,6 +60,10 @@ Supported devices * LPC Peripheral Controller (a subset of subdevices are supported) * Hash/Crypto Engine (HACE) - Hash support only. TODO: HMAC and RSA * ADC + * Secure Boot Controller (AST2600) + * eMMC Boot Controller (dummy) + * PECI Controller (minimal) + * I3C Controller Missing devices @@ -68,12 +75,10 @@ Missing devices * Super I/O Controller * PCI-Express 1 Controller * Graphic Display Controller - * PECI Controller * MCTP Controller * Mailbox Controller * Virtual UART * eSPI Controller - * I3C Controller Boot options ------------ @@ -154,6 +159,8 @@ Supported devices * LPC Peripheral Controller (a subset of subdevices are supported) * Hash/Crypto Engine (HACE) - Hash support only. TODO: HMAC and RSA * ADC + * Secure Boot Controller + * PECI Controller (minimal) Missing devices @@ -161,7 +168,6 @@ Missing devices * PWM and Fan Controller * Slave GPIO Controller - * PECI Controller * Mailbox Controller * Virtual UART * eSPI Controller @@ -182,3 +188,51 @@ To boot a kernel directly from a Zephyr build tree: $ qemu-system-arm -M ast1030-evb -nographic \ -kernel zephyr.elf + +Facebook Yosemite v3.5 Platform and CraterLake Server (``fby35``) +================================================================== + +Facebook has a series of multi-node compute server designs named +Yosemite. The most recent version released was +`Yosemite v3 `__. + +Yosemite v3.5 is an iteration on this design, and is very similar: there's a +baseboard with a BMC, and 4 server slots. The new server board design termed +"CraterLake" includes a Bridge IC (BIC), with room for expansion boards to +include various compute accelerators (video, inferencing, etc). At the moment, +only the first server slot's BIC is included. + +Yosemite v3.5 is itself a sled which fits into a 40U chassis, and 3 sleds +can be fit into a chassis. See `here `__ +for an example. + +In this generation, the BMC is an AST2600 and each BIC is an AST1030. The BMC +runs `OpenBMC `__, and the BIC runs +`OpenBIC `__. + +Firmware images can be retrieved from the Github releases or built from the +source code, see the README's for instructions on that. This image uses the +"fby35" machine recipe from OpenBMC, and the "yv35-cl" target from OpenBIC. +Some reference images can also be found here: + +.. code-block:: bash + + $ wget https://github.com/facebook/openbmc/releases/download/openbmc-e2294ff5d31d/fby35.mtd + $ wget https://github.com/peterdelevoryas/OpenBIC/releases/download/oby35-cl-2022.13.01/Y35BCL.elf + +Since this machine has multiple SoC's, each with their own serial console, the +recommended way to run it is to allocate a pseudoterminal for each serial +console and let the monitor use stdio. Also, starting in a paused state is +useful because it allows you to attach to the pseudoterminals before the boot +process starts. + +.. code-block:: bash + + $ qemu-system-arm -machine fby35 \ + -drive file=fby35.mtd,format=raw,if=mtd \ + -device loader,file=Y35BCL.elf,addr=0,cpu-num=2 \ + -serial pty -serial pty -serial mon:stdio \ + -display none -S + $ screen /dev/tty0 # In a separate TMUX pane, terminal window, etc. + $ screen /dev/tty1 + $ (qemu) c # Start the boot process once screen is setup. diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index 3e626c4b68a..3fd76fa0b4f 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -372,6 +372,31 @@ verbose command lines. However, the recommended way to select vector lengths is to explicitly enable each desired length. Therefore only example's (1), (4), and (6) exhibit recommended uses of the properties. +SME CPU Property Examples +------------------------- + + 1) Disable SME:: + + $ qemu-system-aarch64 -M virt -cpu max,sme=off + + 2) Implicitly enable all vector lengths for the ``max`` CPU type:: + + $ qemu-system-aarch64 -M virt -cpu max + + 3) Only enable the 256-bit vector length:: + + $ qemu-system-aarch64 -M virt -cpu max,sme256=on + + 3) Enable the 256-bit and 1024-bit vector lengths:: + + $ qemu-system-aarch64 -M virt -cpu max,sme256=on,sme1024=on + + 4) Disable the 512-bit vector length. This results in all the other + lengths supported by ``max`` defaulting to enabled + (128, 256, 1024 and 2048):: + + $ qemu-system-aarch64 -M virt -cpu max,sve512=off + SVE User-mode Default Vector Length Property -------------------------------------------- @@ -387,3 +412,34 @@ length supported by QEMU is 256. If this property is set to ``-1`` then the default vector length is set to the maximum possible length. + +SME CPU Properties +================== + +The SME CPU properties are much like the SVE properties: ``sme`` is +used to enable or disable the entire SME feature, and ``sme`` is +used to enable or disable specific vector lengths. Finally, +``sme_fa64`` is used to enable or disable ``FEAT_SME_FA64``, which +allows execution of the "full a64" instruction set while Streaming +SVE mode is enabled. + +SME is not supported by KVM at this time. + +At least one vector length must be enabled when ``sme`` is enabled, +and all vector lengths must be powers of 2. The maximum vector +length supported by qemu is 2048 bits. Otherwise, there are no +additional constraints on the set of vector lengths supported by SME. + +SME User-mode Default Vector Length Property +-------------------------------------------- + +For qemu-aarch64, the cpu propery ``sme-default-vector-length=N`` is +defined to mirror the Linux kernel parameter file +``/proc/sys/abi/sme_default_vector_length``. The default length, ``N``, +is in units of bytes and must be between 16 and 8192. +If not specified, the default vector length is 32. + +As with ``sve-default-vector-length``, if the default length is larger +than the maximum vector length enabled, the actual vector length will +be reduced. If this property is set to ``-1`` then the default vector +length is set to the maximum possible length. diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 83b44100659..8e494c8bea5 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -65,6 +65,10 @@ the following architecture extensions: - FEAT_SHA512 (Advanced SIMD SHA512 instructions) - FEAT_SM3 (Advanced SIMD SM3 instructions) - FEAT_SM4 (Advanced SIMD SM4 instructions) +- FEAT_SME (Scalable Matrix Extension) +- FEAT_SME_FA64 (Full A64 instruction set in Streaming SVE mode) +- FEAT_SME_F64F64 (Double-precision floating-point outer product instructions) +- FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) - FEAT_TLBIOS (TLB invalidate instructions in Outer Shareable domain) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 3d1058a80c0..3b6ba69a9a9 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -126,13 +126,18 @@ ras Set ``on``/``off`` to enable/disable reporting host memory errors to a guest using ACPI and guest external abort exceptions. The default is off. +dtb-randomness + Set ``on``/``off`` to pass random seeds via the guest DTB + rng-seed and kaslr-seed nodes (in both "/chosen" and + "/secure-chosen") to use for features like the random number + generator and address space randomisation. The default is + ``on``. You will want to disable it if your trusted boot chain + will verify the DTB it is passed, since this option causes the + DTB to be non-deterministic. It would be the responsibility of + the firmware to come up with a seed and pass it on if it wants to. + dtb-kaslr-seed - Set ``on``/``off`` to pass a random seed via the guest dtb - kaslr-seed node (in both "/chosen" and /secure-chosen) to use - for features like address space randomisation. The default is - ``on``. You will want to disable it if your trusted boot chain will - verify the DTB it is passed. It would be the responsibility of the - firmware to come up with a seed and pass it on if it wants to. + A deprecated synonym for dtb-randomness. Linux guest kernel configuration """""""""""""""""""""""""""""""" diff --git a/docs/system/devices/canokey.rst b/docs/system/devices/canokey.rst index 169f99b8eb8..c2c58ae3e7c 100644 --- a/docs/system/devices/canokey.rst +++ b/docs/system/devices/canokey.rst @@ -146,16 +146,6 @@ multiple CanoKey QEMU running, namely you can not Also, there is no lock on canokey-file, thus two CanoKey QEMU instance can not read one canokey-file at the same time. -Another limitation is that this device is not compatible with ``qemu-xhci``, -in that this device would hang when there are FIDO2 packets (traffic on -interrupt endpoints). If you do not use FIDO2 then it works as intended, -but for full functionality you should use old uhci/ehci bus and attach canokey -to it, for example - -.. parsed-literal:: - - |qemu_system| -device piix3-usb-uhci,id=uhci -device canokey,bus=uhci.0 - References ========== diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index bcbfe8c490c..a57e4c4e5c4 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -118,8 +118,6 @@ and associated component register access via PCI bars. CXL Switch ~~~~~~~~~~ -Not yet implemented in QEMU. - Here we consider a simple CXL switch with only a single virtual hierarchy. Whilst more complex devices exist, their visibility to a particular host is generally the same as for @@ -137,6 +135,10 @@ BARs. The Upstream Port has the configuration interfaces for the HDM decoders which route incoming memory accesses to the appropriate downstream port. +A CXL switch is created in a similar fashion to PCI switches +by creating an upstream port (cxl-upstream) and a number of +downstream ports on the internal switch bus (cxl-downstream). + CXL Memory Devices - Type 3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ CXL type 3 devices use a PCI class code and are intended to be supported @@ -240,6 +242,62 @@ Notes: they will take the Host Physical Addresses of accesses and map them to their own local Device Physical Address Space (DPA). +Example topology involving a switch:: + + |<------------------SYSTEM PHYSICAL ADDRESS MAP (1)----------------->| + | __________ __________________________________ __________ | + | | | | | | | | + | | CFMW 0 | | CXL Fixed Memory Window 1 | | CFMW 1 | | + | | HB0 only | | Configured to interleave memory | | HB1 only | | + | | | | memory accesses across HB0/HB1 | | | | + | |____x_____| |__________________________________| |__________| | + | | | | + | | | | + | | | + Interleave Decoder | | | + Matches this HB | | | + \_____________| |_____________/ + __________|__________ _____|_______________ + | | | | + | CXL HB 0 | | CXL HB 1 | + | HB IntLv Decoders | | HB IntLv Decoders | + | PCI/CXL Root Bus 0c | | PCI/CXL Root Bus 0d | + | | | | + |___x_________________| |_____________________| + | | | | + | + A HB 0 HDM Decoder + matches this Port + ___________|___ + | Root Port 0 | + | Appears in | + | PCI topology | + | As 0c:00.0 | + |___________x___| + | + | + \_____________________ + | + | + --------------------------------------------------- + | Switch 0 USP as PCI 0d:00.0 | + | USP has HDM decoder which direct traffic to | + | appropiate downstream port | + | Switch BUS appears as 0e | + |x__________________________________________________| + | | | | + | | | | + _____|_________ ______|______ ______|_____ ______|_______ + (4)| x | | | | | | | + | CXL Type3 0 | | CXL Type3 1 | | CXL type3 2| | CLX Type 3 3 | + | | | | | | | | + | PMEM0(Vol LSA)| | PMEM1 (...) | | PMEM2 (...)| | PMEM3 (...) | + | Decoder to go | | | | | | | + | from host PA | | PCI 10:00.0 | | PCI 11:00.0| | PCI 12:00.0 | + | to device PA | | | | | | | + | PCI as 0f:00.0| | | | | | | + |_______________| |_____________| |____________| |______________| + Example command lines --------------------- A very simple setup with just one directly attached CXL Type 3 device:: @@ -279,6 +337,32 @@ the CXL Type3 device directly attached (no switches).:: -device cxl-type3,bus=root_port16,memdev=cxl-mem4,lsa=cxl-lsa4,id=cxl-pmem3 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.targets.1=cxl.2,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k +An example of 4 devices below a switch suitable for 1, 2 or 4 way interleave:: + + qemu-system-aarch64 -M virt,gic-version=3,cxl=on -m 4g,maxmem=8G,slots=8 -cpu max \ + ... + -object memory-backend-file,id=cxl-mem0,share=on,mem-path=/tmp/cxltest.raw,size=256M \ + -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest1.raw,size=256M \ + -object memory-backend-file,id=cxl-mem2,share=on,mem-path=/tmp/cxltest2.raw,size=256M \ + -object memory-backend-file,id=cxl-mem3,share=on,mem-path=/tmp/cxltest3.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa0,share=on,mem-path=/tmp/lsa0.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa1.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa2,share=on,mem-path=/tmp/lsa2.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa3,share=on,mem-path=/tmp/lsa3.raw,size=256M \ + -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ + -device cxl-rp,port=0,bus=cxl.1,id=root_port0,chassis=0,slot=0 \ + -device cxl-rp,port=1,bus=cxl.1,id=root_port1,chassis=0,slot=1 \ + -device cxl-upstream,bus=root_port0,id=us0 \ + -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \ + -device cxl-type3,bus=swport0,memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0,size=256M \ + -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \ + -device cxl-type3,bus=swport1,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1,size=256M \ + -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \ + -device cxl-type3,bus=swport2,memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2,size=256M \ + -device cxl-downstream,port=3,bus=us0,id=swport3,chassis=0,slot=7 \ + -device cxl-type3,bus=swport3,memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3,size=256M \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k + Kernel Configuration Options ---------------------------- diff --git a/docs/system/devices/nvme.rst b/docs/system/devices/nvme.rst index b5acb2a9c19..30f841ef622 100644 --- a/docs/system/devices/nvme.rst +++ b/docs/system/devices/nvme.rst @@ -104,8 +104,8 @@ multipath I/O. .. code-block:: console -device nvme-subsys,id=nvme-subsys-0,nqn=subsys0 - -device nvme,serial=a,subsys=nvme-subsys-0 - -device nvme,serial=b,subsys=nvme-subsys-0 + -device nvme,serial=deadbeef,subsys=nvme-subsys-0 + -device nvme,serial=deadbeef,subsys=nvme-subsys-0 This will create an NVM subsystem with two controllers. Having controllers linked to an ``nvme-subsys`` device allows additional ``nvme-ns`` parameters: @@ -239,3 +239,85 @@ The virtual namespace device supports DIF- and DIX-based protection information to ``1`` to transfer protection information as the first eight bytes of metadata. Otherwise, the protection information is transferred as the last eight bytes. + +Virtualization Enhancements and SR-IOV (Experimental Support) +------------------------------------------------------------- + +The ``nvme`` device supports Single Root I/O Virtualization and Sharing +along with Virtualization Enhancements. The controller has to be linked to +an NVM Subsystem device (``nvme-subsys``) for use with SR-IOV. + +A number of parameters are present (**please note, that they may be +subject to change**): + +``sriov_max_vfs`` (default: ``0``) + Indicates the maximum number of PCIe virtual functions supported + by the controller. Specifying a non-zero value enables reporting of both + SR-IOV and ARI (Alternative Routing-ID Interpretation) capabilities + by the NVMe device. Virtual function controllers will not report SR-IOV. + +``sriov_vq_flexible`` + Indicates the total number of flexible queue resources assignable to all + the secondary controllers. Implicitly sets the number of primary + controller's private resources to ``(max_ioqpairs - sriov_vq_flexible)``. + +``sriov_vi_flexible`` + Indicates the total number of flexible interrupt resources assignable to + all the secondary controllers. Implicitly sets the number of primary + controller's private resources to ``(msix_qsize - sriov_vi_flexible)``. + +``sriov_max_vi_per_vf`` (default: ``0``) + Indicates the maximum number of virtual interrupt resources assignable + to a secondary controller. The default ``0`` resolves to + ``(sriov_vi_flexible / sriov_max_vfs)`` + +``sriov_max_vq_per_vf`` (default: ``0``) + Indicates the maximum number of virtual queue resources assignable to + a secondary controller. The default ``0`` resolves to + ``(sriov_vq_flexible / sriov_max_vfs)`` + +The simplest possible invocation enables the capability to set up one VF +controller and assign an admin queue, an IO queue, and a MSI-X interrupt. + +.. code-block:: console + + -device nvme-subsys,id=subsys0 + -device nvme,serial=deadbeef,subsys=subsys0,sriov_max_vfs=1, + sriov_vq_flexible=2,sriov_vi_flexible=1 + +The minimum steps required to configure a functional NVMe secondary +controller are: + + * unbind flexible resources from the primary controller + +.. code-block:: console + + nvme virt-mgmt /dev/nvme0 -c 0 -r 1 -a 1 -n 0 + nvme virt-mgmt /dev/nvme0 -c 0 -r 0 -a 1 -n 0 + + * perform a Function Level Reset on the primary controller to actually + release the resources + +.. code-block:: console + + echo 1 > /sys/bus/pci/devices/0000:01:00.0/reset + + * enable VF + +.. code-block:: console + + echo 1 > /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs + + * assign the flexible resources to the VF and set it ONLINE + +.. code-block:: console + + nvme virt-mgmt /dev/nvme0 -c 1 -r 1 -a 8 -n 1 + nvme virt-mgmt /dev/nvme0 -c 1 -r 0 -a 8 -n 2 + nvme virt-mgmt /dev/nvme0 -c 1 -r 0 -a 9 -n 0 + + * bind the NVMe driver to the VF + +.. code-block:: console + + echo 0000:01:00.1 > /sys/bus/pci/drivers/nvme/bind \ No newline at end of file diff --git a/docs/system/devices/usb.rst b/docs/system/devices/usb.rst index 872d9167589..f39a88f080f 100644 --- a/docs/system/devices/usb.rst +++ b/docs/system/devices/usb.rst @@ -353,3 +353,44 @@ and also assign it to the correct USB bus in QEMU like this: -device usb-ehci,id=ehci \\ -device usb-host,bus=usb-bus.0,hostbus=3,hostport=1 \\ -device usb-host,bus=ehci.0,hostbus=1,hostport=1 + +``usb-host`` properties for reset behavior +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``guest-reset`` and ``guest-reset-all`` properties control +whenever the guest is allowed to reset the physical usb device on the +host. There are three cases: + +``guest-reset=false`` + The guest is not allowed to reset the (physical) usb device. + +``guest-reset=true,guest-resets-all=false`` + The guest is allowed to reset the device when it is not yet + initialized (aka no usb bus address assigned). Usually this results + in one guest reset being allowed. This is the default behavior. + +``guest-reset=true,guest-resets-all=true`` + The guest is allowed to reset the device as it pleases. + +The reason for this existing are broken usb devices. In theory one +should be able to reset (and re-initialize) usb devices at any time. +In practice that may result in shitty usb device firmware crashing and +the device not responding any more until you power-cycle (aka un-plug +and re-plug) it. + +What works best pretty much depends on the behavior of the specific +usb device at hand, so it's a trial-and-error game. If the default +doesn't work, try another option and see whenever the situation +improves. + +record usb transfers +^^^^^^^^^^^^^^^^^^^^ + +All usb devices have support for recording the usb traffic. This can +be enabled using the ``pcap=`` property, for example: + +``-device usb-mouse,pcap=mouse.pcap`` + +The pcap files are compatible with the linux kernels usbmon. Many +tools, including ``wireshark``, can decode and inspect these trace +files. diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 8e08a29e899..faf6349ea51 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -225,7 +225,7 @@ disconnects: qemu-nbd -f qcow2 file.qcow2 Start a long-running server listening with encryption on port 10810, -and whitelist clients with a specific X.509 certificate to connect to +and allow clients with a specific X.509 certificate to connect to a 1 megabyte subset of a raw file, using the export name 'subset': :: diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index 8b975926637..ea00149a63a 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -77,6 +77,7 @@ Standard options: --export [type=]vhost-user-blk,id=,node-name=,addr.type=unix,addr.path=[,writable=on|off][,logical-block-size=][,num-queues=] --export [type=]vhost-user-blk,id=,node-name=,addr.type=fd,addr.str=[,writable=on|off][,logical-block-size=][,num-queues=] --export [type=]fuse,id=,node-name=,mountpoint=[,growable=on|off][,writable=on|off][,allow-other=on|off|auto] + --export [type=]vduse-blk,id=,node-name=,name=[,writable=on|off][,num-queues=][,queue-size=][,logical-block-size=][,serial=] is a block export definition. ``node-name`` is the block node that should be exported. ``writable`` determines whether or not the export allows write @@ -110,6 +111,27 @@ Standard options: ``allow-other`` to auto (the default) will try enabling this option, and on error fall back to disabling it. + The ``vduse-blk`` export type takes a ``name`` (must be unique across the host) + to create the VDUSE device. + ``num-queues`` sets the number of virtqueues (the default is 1). + ``queue-size`` sets the virtqueue descriptor table size (the default is 256). + + The instantiated VDUSE device must then be added to the vDPA bus using the + vdpa(8) command from the iproute2 project:: + + # vdpa dev add name mgmtdev vduse + + The device can be removed from the vDPA bus later as follows:: + + # vdpa dev del + + For more information about attaching vDPA devices to the host with + virtio_vdpa.ko or attaching them to guests with vhost_vdpa.ko, see + https://vdpa-dev.gitlab.io/. + + For more information about VDUSE, see + https://docs.kernel.org/userspace-api/vduse.html. + .. option:: --monitor MONITORDEF is a QMP monitor definition. See the :manpage:`qemu(1)` manual page for diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c index 118c68da831..cee658c158b 100644 --- a/ebpf/ebpf_rss.c +++ b/ebpf/ebpf_rss.c @@ -49,7 +49,7 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx) goto error; } - bpf_program__set_socket_filter(rss_bpf_ctx->progs.tun_rss_steering_prog); + bpf_program__set_type(rss_bpf_ctx->progs.tun_rss_steering_prog, BPF_PROG_TYPE_SOCKET_FILTER); if (rss_bpf__load(rss_bpf_ctx)) { trace_ebpf_error("eBPF RSS", "can not load RSS program"); diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 943e3301d20..1610472cfc4 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -390,7 +390,8 @@ bool float32_is_signaling_nan(float32 a_, float_status *status) static int pickNaN(FloatClass a_cls, FloatClass b_cls, bool aIsLargerSignificand, float_status *status) { -#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) +#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) || \ + defined(TARGET_LOONGARCH64) || defined(TARGET_S390X) /* ARM mandated NaN propagation rules (see FPProcessNaNs()), take * the first of: * 1. A if it is signaling @@ -574,6 +575,29 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 1; } } +#elif defined(TARGET_LOONGARCH64) + /* + * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + if (infzero) { + float_raise(float_flag_invalid | float_flag_invalid_imz, status); + return 2; + } + /* Prefer sNaN over qNaN, in the c, a, b order. */ + if (is_snan(c_cls)) { + return 2; + } else if (is_snan(a_cls)) { + return 0; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_qnan(c_cls)) { + return 2; + } else if (is_qnan(a_cls)) { + return 0; + } else { + return 1; + } #elif defined(TARGET_PPC) /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating diff --git a/gdbstub.c b/gdbstub.c index 22d0247efed..2f44f7b525b 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1878,14 +1878,46 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) static void handle_file_io(GArray *params, void *user_ctx) { if (params->len >= 1 && gdbserver_state.current_syscall_cb) { - target_ulong ret, err; + uint64_t ret; + int err; - ret = (target_ulong)get_param(params, 0)->val_ull; + ret = get_param(params, 0)->val_ull; if (params->len >= 2) { - err = (target_ulong)get_param(params, 1)->val_ull; + err = get_param(params, 1)->val_ull; } else { err = 0; } + + /* Convert GDB error numbers back to host error numbers. */ +#define E(X) case GDB_E##X: err = E##X; break + switch (err) { + case 0: + break; + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(ACCES); + E(FAULT); + E(BUSY); + E(EXIST); + E(NODEV); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + err = EINVAL; + break; + } +#undef E + gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); gdbserver_state.current_syscall_cb = NULL; } @@ -2151,22 +2183,6 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) put_strbuf(); } -#ifdef CONFIG_USER_ONLY -static void handle_query_offsets(GArray *params, void *user_ctx) -{ - TaskState *ts; - - ts = gdbserver_state.c_cpu->opaque; - g_string_printf(gdbserver_state.str_buf, - "Text=" TARGET_ABI_FMT_lx - ";Data=" TARGET_ABI_FMT_lx - ";Bss=" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - put_strbuf(); -} - //// --- Begin LibAFL code --- struct libafl_custom_gdb_cmd { @@ -2195,6 +2211,26 @@ void libafl_qemu_gdb_reply(const char* buf, size_t len) put_packet(hex_buf->str); } +//// --- End LibAFL code --- + +#ifdef CONFIG_USER_ONLY +static void handle_query_offsets(GArray *params, void *user_ctx) +{ + TaskState *ts; + + ts = gdbserver_state.c_cpu->opaque; + g_string_printf(gdbserver_state.str_buf, + "Text=" TARGET_ABI_FMT_lx + ";Data=" TARGET_ABI_FMT_lx + ";Bss=" TARGET_ABI_FMT_lx, + ts->info->code_offset, + ts->info->data_offset, + ts->info->data_offset); + put_strbuf(); +} + +//// --- Begin LibAFL code --- + static void handle_query_rcmd(GArray *params, void *user_ctx) { // const guint8 zero = 0; @@ -2251,6 +2287,23 @@ static void handle_query_rcmd(GArray *params, void *user_ctx) g_assert(gdbserver_state.mem_buf->len == 0); len = len / 2; hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + +//// --- Begin LibAFL code --- + + struct libafl_custom_gdb_cmd** c = &libafl_qemu_gdb_cmds; + int recognized = 0; + while (*c) { + recognized |= (*c)->callback(gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, (*c)->data); + c = &(*c)->next; + } + + if (recognized) { + put_packet("OK"); + return; + } + +//// --- End LibAFL code --- + g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 834bed089e7..188d9ece3b2 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -865,6 +865,19 @@ SRST Display the vcpu dirty rate information. ERST + { + .name = "vcpu_dirty_limit", + .args_type = "", + .params = "", + .help = "show dirty page limit information of all vCPU", + .cmd = hmp_info_vcpu_dirty_limit, + }, + +SRST + ``info vcpu_dirty_limit`` + Display the vcpu dirty page limit information. +ERST + #if defined(TARGET_I386) { .name = "sgx", @@ -894,3 +907,17 @@ SRST ``info via`` Show guest mos6522 VIA devices. ERST + + { + .name = "stats", + .args_type = "target:s,names:s?,provider:s?", + .params = "target [names] [provider]", + .help = "show statistics for the given target (vm or vcpu); optionally filter by" + "name (comma-separated list, or * for all) and provider", + .cmd = hmp_info_stats, + }, + +SRST + ``stats`` + Show runtime-collected statistics +ERST diff --git a/hmp-commands.hx b/hmp-commands.hx index 564f1de364d..182e639d149 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -78,6 +78,7 @@ ERST .help = "resize a block image", .cmd = hmp_block_resize, .coroutine = true, + .flags = "p", }, SRST @@ -94,6 +95,7 @@ ERST .params = "device [speed [base]]", .help = "copy data from a backing file into a block device", .cmd = hmp_block_stream, + .flags = "p", }, SRST @@ -107,6 +109,7 @@ ERST .params = "device speed", .help = "set maximum speed for a background block operation", .cmd = hmp_block_job_set_speed, + .flags = "p", }, SRST @@ -122,6 +125,7 @@ ERST "\n\t\t\t if you want to abort the operation immediately" "\n\t\t\t instead of keep running until data is in sync)", .cmd = hmp_block_job_cancel, + .flags = "p", }, SRST @@ -135,6 +139,7 @@ ERST .params = "device", .help = "stop an active background block operation", .cmd = hmp_block_job_complete, + .flags = "p", }, SRST @@ -149,6 +154,7 @@ ERST .params = "device", .help = "pause an active background block operation", .cmd = hmp_block_job_pause, + .flags = "p", }, SRST @@ -162,6 +168,7 @@ ERST .params = "device", .help = "resume a paused background block operation", .cmd = hmp_block_job_resume, + .flags = "p", }, SRST @@ -1406,6 +1413,7 @@ ERST .params = "nbd_server_start [-a] [-w] host:port", .help = "serve block devices on the given host and port", .cmd = hmp_nbd_server_start, + .flags = "p", }, SRST ``nbd_server_start`` *host*:*port* @@ -1421,6 +1429,7 @@ ERST .params = "nbd_server_add [-w] device [name]", .help = "export a block device via NBD", .cmd = hmp_nbd_server_add, + .flags = "p", }, SRST ``nbd_server_add`` *device* [ *name* ] @@ -1436,6 +1445,7 @@ ERST .params = "nbd_server_remove [-f] name", .help = "remove an export previously exposed via NBD", .cmd = hmp_nbd_server_remove, + .flags = "p", }, SRST ``nbd_server_remove [-f]`` *name* @@ -1452,6 +1462,7 @@ ERST .params = "nbd_server_stop", .help = "stop serving block devices using the NBD protocol", .cmd = hmp_nbd_server_stop, + .flags = "p", }, SRST ``nbd_server_stop`` @@ -1481,6 +1492,7 @@ ERST .params = "getfd name", .help = "receive a file descriptor via SCM rights and assign it a name", .cmd = hmp_getfd, + .flags = "p", }, SRST @@ -1496,6 +1508,7 @@ ERST .params = "closefd name", .help = "close a file descriptor previously passed via SCM rights", .cmd = hmp_closefd, + .flags = "p", }, SRST @@ -1511,6 +1524,7 @@ ERST .params = "device bps bps_rd bps_wr iops iops_rd iops_wr", .help = "change I/O throttle limits for a block drive", .cmd = hmp_block_set_io_throttle, + .flags = "p", }, SRST @@ -1754,3 +1768,35 @@ ERST "\n\t\t\t -b to specify dirty bitmap as method of calculation)", .cmd = hmp_calc_dirty_rate, }, + +SRST +``set_vcpu_dirty_limit`` + Set dirty page rate limit on virtual CPU, the information about all the + virtual CPU dirty limit status can be observed with ``info vcpu_dirty_limit`` + command. +ERST + + { + .name = "set_vcpu_dirty_limit", + .args_type = "dirty_rate:l,cpu_index:l?", + .params = "dirty_rate [cpu_index]", + .help = "set dirty page rate limit, use cpu_index to set limit" + "\n\t\t\t\t\t on a specified virtual cpu", + .cmd = hmp_set_vcpu_dirty_limit, + }, + +SRST +``cancel_vcpu_dirty_limit`` + Cancel dirty page rate limit on virtual CPU, the information about all the + virtual CPU dirty limit status can be observed with ``info vcpu_dirty_limit`` + command. +ERST + + { + .name = "cancel_vcpu_dirty_limit", + .args_type = "cpu_index:l?", + .params = "[cpu_index]", + .help = "cancel dirty page rate limit, use cpu_index to cancel" + "\n\t\t\t\t\t limit on a specified virtual cpu", + .cmd = hmp_cancel_vcpu_dirty_limit, + }, diff --git a/hw/9pfs/9p-xattr-user.c b/hw/9pfs/9p-xattr-user.c index f2ae9582e61..535677ed609 100644 --- a/hw/9pfs/9p-xattr-user.c +++ b/hw/9pfs/9p-xattr-user.c @@ -27,7 +27,7 @@ static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, { if (strncmp(name, "user.virtfs.", 12) == 0) { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ errno = ENOATTR; @@ -49,7 +49,7 @@ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, name_size -= 12; } else { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ return 0; @@ -74,7 +74,7 @@ static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, { if (strncmp(name, "user.virtfs.", 12) == 0) { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ errno = EACCES; @@ -88,7 +88,7 @@ static int mp_user_removexattr(FsContext *ctx, { if (strncmp(name, "user.virtfs.", 12) == 0) { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ errno = EACCES; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 0cd0c14c2aa..aebadeaa037 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1766,9 +1766,9 @@ static bool same_stat_id(const struct stat *a, const struct stat *b) static void coroutine_fn v9fs_walk(void *opaque) { - int name_idx; + int name_idx, nwalked; g_autofree V9fsQID *qids = NULL; - int i, err = 0; + int i, err = 0, any_err = 0; V9fsPath dpath, path; P9ARRAY_REF(V9fsPath) pathes = NULL; uint16_t nwnames; @@ -1834,54 +1834,61 @@ static void coroutine_fn v9fs_walk(void *opaque) * driver code altogether inside the following block. */ v9fs_co_run_in_worker({ + nwalked = 0; if (v9fs_request_cancelled(pdu)) { - err = -EINTR; + any_err |= err = -EINTR; break; } err = s->ops->lstat(&s->ctx, &dpath, &fidst); if (err < 0) { - err = -errno; + any_err |= err = -errno; break; } stbuf = fidst; - for (name_idx = 0; name_idx < nwnames; name_idx++) { + for (; nwalked < nwnames; nwalked++) { if (v9fs_request_cancelled(pdu)) { - err = -EINTR; + any_err |= err = -EINTR; break; } if (!same_stat_id(&pdu->s->root_st, &stbuf) || - strcmp("..", wnames[name_idx].data)) + strcmp("..", wnames[nwalked].data)) { err = s->ops->name_to_path(&s->ctx, &dpath, - wnames[name_idx].data, - &pathes[name_idx]); + wnames[nwalked].data, + &pathes[nwalked]); if (err < 0) { - err = -errno; + any_err |= err = -errno; break; } if (v9fs_request_cancelled(pdu)) { - err = -EINTR; + any_err |= err = -EINTR; break; } - err = s->ops->lstat(&s->ctx, &pathes[name_idx], &stbuf); + err = s->ops->lstat(&s->ctx, &pathes[nwalked], &stbuf); if (err < 0) { - err = -errno; + any_err |= err = -errno; break; } - stbufs[name_idx] = stbuf; - v9fs_path_copy(&dpath, &pathes[name_idx]); + stbufs[nwalked] = stbuf; + v9fs_path_copy(&dpath, &pathes[nwalked]); } } }); /* * Handle all the rest of this Twalk request on main thread ... + * + * NOTE: -EINTR is an exception where we deviate from the protocol spec + * and simply send a (R)Lerror response instead of bothering to assemble + * a (deducted) Rwalk response; because -EINTR is always the result of a + * Tflush request, so client would no longer wait for a response in this + * case anyway. */ - if (err < 0) { + if ((err < 0 && !nwalked) || err == -EINTR) { goto out; } - err = stat_to_qid(pdu, &fidst, &qid); - if (err < 0) { + any_err |= err = stat_to_qid(pdu, &fidst, &qid); + if (err < 0 && !nwalked) { goto out; } stbuf = fidst; @@ -1890,20 +1897,29 @@ static void coroutine_fn v9fs_walk(void *opaque) v9fs_path_copy(&dpath, &fidp->path); v9fs_path_copy(&path, &fidp->path); - for (name_idx = 0; name_idx < nwnames; name_idx++) { + for (name_idx = 0; name_idx < nwalked; name_idx++) { if (!same_stat_id(&pdu->s->root_st, &stbuf) || strcmp("..", wnames[name_idx].data)) { stbuf = stbufs[name_idx]; - err = stat_to_qid(pdu, &stbuf, &qid); + any_err |= err = stat_to_qid(pdu, &stbuf, &qid); if (err < 0) { - goto out; + break; } v9fs_path_copy(&path, &pathes[name_idx]); v9fs_path_copy(&dpath, &path); } memcpy(&qids[name_idx], &qid, sizeof(qid)); } + if (any_err < 0) { + if (!name_idx) { + /* don't send any QIDs, send Rlerror instead */ + goto out; + } else { + /* send QIDs (not Rlerror), but fid MUST remain unaffected */ + goto send_qids; + } + } if (fid == newfid) { if (fidp->fid_type != P9_FID_NONE) { err = -EINVAL; @@ -1921,8 +1937,9 @@ static void coroutine_fn v9fs_walk(void *opaque) newfidp->uid = fidp->uid; v9fs_path_copy(&newfidp->path, &path); } - err = v9fs_walk_marshal(pdu, nwnames, qids); - trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); +send_qids: + err = v9fs_walk_marshal(pdu, name_idx, qids); + trace_v9fs_walk_return(pdu->tag, pdu->id, name_idx, qids); out: put_fid(pdu, fidp); if (newfidp) { diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index de509c2b48f..df856b2669a 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -440,6 +440,7 @@ static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) (record_size >= 4096) /* PAGE_SIZE */ )) { error_setg(errp, "ERST record_size %u is invalid", record_size); + return; } /* Validity check header */ @@ -450,6 +451,7 @@ static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) (le16_to_cpu(header->reserved) == 0) )) { error_setg(errp, "ERST backend storage header is invalid"); + return; } /* Check storage_size against record_size */ @@ -457,6 +459,7 @@ static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) (record_size > s->storage_size)) { error_setg(errp, "ACPI ERST requires storage size be multiple of " "record size (%uKiB)", record_size); + return; } /* Compute offset of first and last record storage slot */ diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 0d43da19ea4..5f85b163270 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -476,7 +476,7 @@ struct NvdimmFuncGetLabelDataOut { /* the size of buffer filled by QEMU. */ uint32_t len; uint32_t func_ret_status; /* return status code. */ - uint8_t out_buf[]; /* the data got via Get Namesapce Label function. */ + uint8_t out_buf[]; /* the data got via Get Namespace Label function. */ } QEMU_PACKED; typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index bf65bbea494..84d75e6b846 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -192,8 +192,12 @@ static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) * ACPI doesn't allow hotplug of bridge devices. Don't allow * hot-unplug of bridge devices unless they were added by hotplug * (and so, not described by acpi). + * + * Don't allow hot-unplug of SR-IOV Virtual Functions, as they + * will be removed implicitly, when Physical Function is unplugged. */ - return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable; + return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable || + pci_is_vf(dev); } static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index 0f0a9f63e20..bc6f3f55e64 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -36,7 +36,7 @@ REG32(NPCM7XX_ADC_DATA, 0x4) #define NPCM7XX_ADC_CON_INT BIT(18) #define NPCM7XX_ADC_CON_EN BIT(17) #define NPCM7XX_ADC_CON_RST BIT(16) -#define NPCM7XX_ADC_CON_CONV BIT(14) +#define NPCM7XX_ADC_CON_CONV BIT(13) #define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8) #define NPCM7XX_ADC_MAX_RESULT 1023 @@ -242,7 +242,7 @@ static void npcm7xx_adc_init(Object *obj) for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) { object_property_add_uint32_ptr(obj, "adci[*]", - &s->adci[i], OBJ_PROP_FLAG_WRITE); + &s->adci[i], OBJ_PROP_FLAG_READWRITE); } object_property_add_uint32_ptr(obj, "vref", &s->vref, OBJ_PROP_FLAG_WRITE); diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 219262a8da3..15fa79afd33 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -455,6 +455,8 @@ config ASPEED_SOC select EMC141X select UNIMP select LED + select PMBUS + select MAX31785 config MPS2 bool diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 318ed4348c7..308ed155525 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -174,7 +174,7 @@ void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk) const int64_t rom_size = 32 * KiB; g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); - if (blk_pread(blk, 8 * KiB, buffer, rom_size) < 0) { + if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { error_setg(&error_fatal, "%s: failed to read BlockBackend data", __func__); return; diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 98dc185acd9..4193a3d23d1 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -26,6 +26,7 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "hw/qdev-clock.h" +#include "sysemu/sysemu.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ @@ -174,26 +175,9 @@ struct AspeedMachineState { #define BLETCHLEY_BMC_HW_STRAP1 AST2600_EVB_HW_STRAP1 #define BLETCHLEY_BMC_HW_STRAP2 AST2600_EVB_HW_STRAP2 -/* - * The max ram region is for firmwares that scan the address space - * with load/store to guess how much RAM the SoC has. - */ -static uint64_t max_ram_read(void *opaque, hwaddr offset, unsigned size) -{ - return 0; -} - -static void max_ram_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - /* Discard writes */ -} - -static const MemoryRegionOps max_ram_ops = { - .read = max_ram_read, - .write = max_ram_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; +/* Qualcomm DC-SCM hardware value */ +#define QCOM_DC_SCM_V1_BMC_HW_STRAP1 0x00000000 +#define QCOM_DC_SCM_V1_BMC_HW_STRAP2 0x00000041 #define AST_SMP_MAILBOX_BASE 0x1e6e2180 #define AST_SMP_MBOX_FIELD_ENTRY (AST_SMP_MAILBOX_BASE + 0x0) @@ -270,7 +254,7 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, } storage = g_malloc0(rom_size); - if (blk_pread(blk, 0, storage, rom_size) < 0) { + if (blk_pread(blk, 0, rom_size, storage, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } @@ -278,7 +262,7 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); } -static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, +void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, unsigned int count, int unit0) { int i; @@ -318,26 +302,37 @@ static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) &error_fatal); } +static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) +{ + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); + AspeedSoCState *s = &bmc->soc; + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + aspeed_soc_uart_set_chr(s, amc->uart_default, serial_hd(0)); + for (int i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { + if (uart == amc->uart_default) { + continue; + } + aspeed_soc_uart_set_chr(s, uart, serial_hd(i)); + } +} + static void aspeed_machine_init(MachineState *machine) { AspeedMachineState *bmc = ASPEED_MACHINE(machine); AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); AspeedSoCClass *sc; DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); - ram_addr_t max_ram_size; int i; NICInfo *nd = &nd_table[0]; - memory_region_init(&bmc->ram_container, NULL, "aspeed-ram-container", - 4 * GiB); - memory_region_add_subregion(&bmc->ram_container, 0, machine->ram); - object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); sc = ASPEED_SOC_GET_CLASS(&bmc->soc); /* - * This will error out if isize is not supported by memory controller. + * This will error out if the RAM size is not supported by the + * memory controller of the SoC. */ object_property_set_uint(OBJECT(&bmc->soc), "ram-size", machine->ram_size, &error_fatal); @@ -354,6 +349,8 @@ static void aspeed_machine_init(MachineState *machine) &error_abort); object_property_set_int(OBJECT(&bmc->soc), "hw-strap2", amc->hw_strap2, &error_abort); + object_property_set_link(OBJECT(&bmc->soc), "memory", + OBJECT(get_system_memory()), &error_abort); object_property_set_link(OBJECT(&bmc->soc), "dram", OBJECT(machine->ram), &error_abort); if (machine->kernel_filename) { @@ -365,20 +362,9 @@ static void aspeed_machine_init(MachineState *machine) object_property_set_int(OBJECT(&bmc->soc), "hw-prot-key", ASPEED_SCU_PROT_KEY, &error_abort); } - qdev_prop_set_uint32(DEVICE(&bmc->soc), "uart-default", - amc->uart_default); + connect_serial_hds_to_uarts(bmc); qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); - memory_region_add_subregion(get_system_memory(), - sc->memmap[ASPEED_DEV_SDRAM], - &bmc->ram_container); - - max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size", - &error_abort); - memory_region_init_io(&bmc->max_ram, NULL, &max_ram_ops, NULL, - "max_ram", max_ram_size - machine->ram_size); - memory_region_add_subregion(&bmc->ram_container, machine->ram_size, &bmc->max_ram); - aspeed_board_init_flashes(&bmc->soc.fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, amc->num_cs, 0); @@ -519,10 +505,6 @@ static void ast2500_evb_i2c_init(AspeedMachineState *bmc) /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105, 0x4d); - - /* The AST2500 EVB does not have an RTC. Let's pretend that one is - * plugged on the I2C bus header */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "ds1338", 0x32); } static void ast2600_evb_i2c_init(AspeedMachineState *bmc) @@ -615,7 +597,6 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) LEDState *led; /* Bus 3: TODO bmp280@77 */ - /* Bus 3: TODO max31785@52 */ dev = DEVICE(i2c_slave_new(TYPE_PCA9552, 0x60)); qdev_prop_set_string(dev, "description", "pca1"); i2c_slave_realize_and_unref(I2C_SLAVE(dev), @@ -631,6 +612,7 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) qdev_get_gpio_in(DEVICE(led), 0)); } i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "dps310", 0x76); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "max31785", 0x52); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "tmp423", 0x4c); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), "tmp423", 0x4c); @@ -775,13 +757,13 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) create_pca9552(soc, 7, 0x31); create_pca9552(soc, 7, 0x32); create_pca9552(soc, 7, 0x33); - /* Bus 7: TODO max31785@52 */ create_pca9552(soc, 7, 0x60); create_pca9552(soc, 7, 0x61); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "dps310", 0x76); /* Bus 7: TODO si7021-a20@20 */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105, 0x48); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "max31785", 0x52); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, 64 * KiB); aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x51, 64 * KiB); @@ -988,6 +970,45 @@ static void fby35_i2c_init(AspeedMachineState *bmc) */ } +static void qcom_dc_scm_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 15), "tmp105", 0x4d); +} + +static void qcom_dc_scm_firework_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + I2CSlave *therm_mux, *cpuvr_mux; + + /* Create the generic DC-SCM hardware */ + qcom_dc_scm_bmc_i2c_init(bmc); + + /* Now create the Firework specific hardware */ + + /* I2C7 CPUVR MUX */ + cpuvr_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), + "pca9546", 0x70); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 0), "pca9548", 0x72); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 1), "pca9548", 0x72); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 2), "pca9548", 0x72); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 3), "pca9548", 0x72); + + /* I2C8 Thermal Diodes*/ + therm_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), + "pca9548", 0x70); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 0), TYPE_LM75, 0x4C); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 1), TYPE_LM75, 0x4C); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 2), TYPE_LM75, 0x48); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 3), TYPE_LM75, 0x48); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 4), TYPE_LM75, 0x48); + + /* I2C9 Fan Controller (MAX31785) */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "max31785", 0x52); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "max31785", 0x54); +} + static bool aspeed_get_mmio_exec(Object *obj, Error **errp) { return ASPEED_MACHINE(obj)->mmio_exec; @@ -1337,11 +1358,23 @@ static void fby35_reset(MachineState *state) qemu_devices_reset(); - /* Board ID */ + /* Board ID: 7 (Class-1, 4 slots) */ object_property_set_bool(OBJECT(gpio), "gpioV4", true, &error_fatal); object_property_set_bool(OBJECT(gpio), "gpioV5", true, &error_fatal); object_property_set_bool(OBJECT(gpio), "gpioV6", true, &error_fatal); object_property_set_bool(OBJECT(gpio), "gpioV7", false, &error_fatal); + + /* Slot presence pins, inverse polarity. (False means present) */ + object_property_set_bool(OBJECT(gpio), "gpioH4", false, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioH5", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioH6", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioH7", true, &error_fatal); + + /* Slot 12v power pins, normal polarity. (True means powered-on) */ + object_property_set_bool(OBJECT(gpio), "gpioB2", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioB3", false, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioB4", false, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioB5", false, &error_fatal); } static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) @@ -1375,8 +1408,9 @@ static void aspeed_minibmc_machine_init(MachineState *machine) object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); qdev_connect_clock_in(DEVICE(&bmc->soc), "sysclk", sysclk); - qdev_prop_set_uint32(DEVICE(&bmc->soc), "uart-default", - amc->uart_default); + 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, @@ -1401,6 +1435,18 @@ static void aspeed_minibmc_machine_init(MachineState *machine) AST1030_INTERNAL_FLASH_SIZE); } +static void ast1030_evb_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + /* U10 24C08 connects to SDA/SCL Groupt 1 by default */ + uint8_t *eeprom_buf = g_malloc0(32 * 1024); + smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 0), 0x50, eeprom_buf); + + /* U11 LM75 connects to SDA/SCL Group 2 by default */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 1), "tmp105", 0x4d); +} + static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, void *data) { @@ -1412,6 +1458,7 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, amc->hw_strap1 = 0; amc->hw_strap2 = 0; mc->init = aspeed_minibmc_machine_init; + amc->i2c_init = ast1030_evb_i2c_init; mc->default_ram_size = 0; mc->default_cpus = mc->min_cpus = mc->max_cpus = 1; amc->fmc_model = "sst25vf032b"; @@ -1420,6 +1467,46 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, amc->macs_mask = 0; } +static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, + void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Qualcomm DC-SCM V1 BMC (Cortex A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = QCOM_DC_SCM_V1_BMC_HW_STRAP1; + amc->hw_strap2 = QCOM_DC_SCM_V1_BMC_HW_STRAP2; + amc->fmc_model = "n25q512a"; + amc->spi_model = "n25q512a"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; + amc->i2c_init = qcom_dc_scm_bmc_i2c_init; + mc->default_ram_size = 1 * GiB; + mc->default_cpus = mc->min_cpus = mc->max_cpus = + aspeed_soc_num_cpus(amc->soc_name); +}; + +static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, + void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Qualcomm DC-SCM V1/Firework BMC (Cortex A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = QCOM_DC_SCM_V1_BMC_HW_STRAP1; + amc->hw_strap2 = QCOM_DC_SCM_V1_BMC_HW_STRAP2; + amc->fmc_model = "n25q512a"; + amc->spi_model = "n25q512a"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; + amc->i2c_init = qcom_dc_scm_firework_i2c_init; + mc->default_ram_size = 1 * GiB; + mc->default_cpus = mc->min_cpus = mc->max_cpus = + aspeed_soc_num_cpus(amc->soc_name); +}; + static const TypeInfo aspeed_machine_types[] = { { .name = MACHINE_TYPE_NAME("palmetto-bmc"), @@ -1457,6 +1544,14 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("g220a-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_g220a_class_init, + }, { + .name = MACHINE_TYPE_NAME("qcom-dc-scm-v1-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_qcom_dc_scm_v1_class_init, + }, { + .name = MACHINE_TYPE_NAME("qcom-firework-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_qcom_firework_class_init, }, { .name = MACHINE_TYPE_NAME("fp5280g2-bmc"), .parent = TYPE_ASPEED_MACHINE, diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index d5345416840..4d0b9b115f0 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -47,6 +47,7 @@ static const hwaddr aspeed_soc_ast1030_memmap[] = { [ASPEED_DEV_UART13] = 0x7E790700, [ASPEED_DEV_WDT] = 0x7E785000, [ASPEED_DEV_LPC] = 0x7E789000, + [ASPEED_DEV_PECI] = 0x7E78B000, [ASPEED_DEV_I2C] = 0x7E7B0000, }; @@ -75,6 +76,7 @@ static const int aspeed_soc_ast1030_irqmap[] = { [ASPEED_DEV_TIMER8] = 23, [ASPEED_DEV_WDT] = 24, [ASPEED_DEV_LPC] = 35, + [ASPEED_DEV_PECI] = 38, [ASPEED_DEV_FMC] = 39, [ASPEED_DEV_PWM] = 44, [ASPEED_DEV_ADC] = 46, @@ -114,6 +116,9 @@ static void aspeed_soc_ast1030_init(Object *obj) object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), "hw-strap1"); object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), "hw-strap2"); + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); + object_initialize_child(obj, "i2c", &s->i2c, typename); + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); @@ -130,6 +135,8 @@ static void aspeed_soc_ast1030_init(Object *obj) object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); + object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); for (i = 0; i < sc->wdts_num; i++) { @@ -137,18 +144,26 @@ static void aspeed_soc_ast1030_init(Object *obj) object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); } + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); object_initialize_child(obj, "gpio", &s->gpio, typename); + + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "sbc-unimplemented", &s->sbc_unimplemented, + TYPE_UNIMPLEMENTED_DEVICE); } static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) { AspeedSoCState *s = ASPEED_SOC(dev_soc); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - MemoryRegion *system_memory = get_system_memory(); DeviceState *armv7m; Error *err = NULL; int i; + g_autofree char *sram_name = NULL; if (!clock_has_source(s->sysclk)) { error_setg(errp, "sysclk clock must be wired up by the board code"); @@ -156,12 +171,12 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } /* General I/O memory space to catch all unimplemented device */ - create_unimplemented_device("aspeed.sbc", - sc->memmap[ASPEED_DEV_SBC], - 0x40000); - create_unimplemented_device("aspeed.io", - sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->sbc_unimplemented), + "aspeed.sbc", sc->memmap[ASPEED_DEV_SBC], + 0x40000); /* AST1030 CPU Core */ armv7m = DEVICE(&s->armv7m); @@ -169,16 +184,18 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) qdev_prop_set_string(armv7m, "cpu-type", sc->cpu_type); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); object_property_set_link(OBJECT(&s->armv7m), "memory", - OBJECT(system_memory), &error_abort); + OBJECT(s->memory), &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), &error_abort); /* Internal SRAM */ - memory_region_init_ram(&s->sram, NULL, "aspeed.sram", sc->sram_size, &err); + sram_name = g_strdup_printf("aspeed.sram.%d", + CPU(s->armv7m.cpu)->cpu_index); + memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, &err); if (err != NULL) { error_propagate(errp, err); return; } - memory_region_add_subregion(system_memory, + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); @@ -186,13 +203,37 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* I2C */ + + object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(&s->sram), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->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); + } + + /* PECI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + sc->memmap[ASPEED_DEV_PECI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); /* LPC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -218,7 +259,9 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); /* UART */ - aspeed_soc_uart_init(s); + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), @@ -226,7 +269,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -237,7 +280,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); @@ -247,8 +290,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -260,9 +303,9 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -270,7 +313,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { @@ -281,7 +324,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); } @@ -289,7 +332,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); } diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index b0a4199b696..aa2cd90bec1 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -59,6 +59,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_LPC] = 0x1E789000, [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, + [ASPEED_DEV_PECI] = 0x1E78B000, [ASPEED_DEV_UART1] = 0x1E783000, [ASPEED_DEV_UART2] = 0x1E78D000, [ASPEED_DEV_UART3] = 0x1E78E000, @@ -122,6 +123,7 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_LPC] = 35, [ASPEED_DEV_IBT] = 143, [ASPEED_DEV_I2C] = 110, /* 110 -> 125 */ + [ASPEED_DEV_PECI] = 38, [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, [ASPEED_DEV_HACE] = 4, @@ -180,6 +182,8 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); @@ -197,8 +201,6 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "sdmc", &s->sdmc, typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size"); - object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), - "max-ram-size"); for (i = 0; i < sc->wdts_num; i++) { snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); @@ -212,6 +214,10 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "mii[*]", &s->mii[i], TYPE_ASPEED_MII); } + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + snprintf(typename, sizeof(typename), TYPE_ASPEED_XDMA "-%s", socname); object_initialize_child(obj, "xdma", &s->xdma, typename); @@ -248,6 +254,13 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C); object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); + + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "dpmcu", &s->dpmcu, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "emmc-boot-controller", + &s->emmc_boot_controller, + TYPE_UNIMPLEMENTED_DEVICE); } /* @@ -267,19 +280,21 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); Error *err = NULL; qemu_irq irq; + g_autofree char *sram_name = NULL; /* IO space */ - create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); /* Video engine stub */ - create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], - 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->video), "aspeed.video", + sc->memmap[ASPEED_DEV_VIDEO], 0x1000); /* eMMC Boot Controller stub */ - create_unimplemented_device("aspeed.emmc-boot-controller", - sc->memmap[ASPEED_DEV_EMMC_BC], - 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->emmc_boot_controller), + "aspeed.emmc-boot-controller", + sc->memmap[ASPEED_DEV_EMMC_BC], 0x1000); /* CPU */ for (i = 0; i < sc->num_cpus; i++) { @@ -292,6 +307,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 1125000000, &error_abort); + object_property_set_link(OBJECT(&s->cpu[i]), "memory", + OBJECT(s->memory), &error_abort); if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { return; @@ -306,11 +323,11 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); for (i = 0; i < sc->num_cpus; i++) { SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); - DeviceState *d = DEVICE(qemu_get_cpu(i)); + DeviceState *d = DEVICE(&s->cpu[i]); irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); sysbus_connect_irq(sbd, i, irq); @@ -323,30 +340,31 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } /* SRAM */ - memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", - sc->sram_size, &err); + 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(get_system_memory(), + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); /* DPMCU */ - create_unimplemented_device("aspeed.dpmcu", sc->memmap[ASPEED_DEV_DPMCU], - ASPEED_SOC_DPMCU_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), "aspeed.dpmcu", + sc->memmap[ASPEED_DEV_DPMCU], + ASPEED_SOC_DPMCU_SIZE); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* RTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -356,7 +374,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -367,12 +385,14 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); /* UART */ - aspeed_soc_uart_init(s); + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -380,7 +400,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[ASPEED_DEV_I2C] + i); @@ -388,14 +408,23 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); } + /* PECI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + sc->memmap[ASPEED_DEV_PECI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -407,9 +436,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -418,7 +447,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -428,7 +457,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + sc->memmap[ASPEED_DEV_SDMC]); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { @@ -439,10 +469,15 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); } + /* RAM */ + if (!aspeed_soc_dram_init(s, errp)) { + return; + } + /* Net */ for (i = 0; i < sc->macs_num; i++) { object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, @@ -450,7 +485,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -461,7 +496,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->mii[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->mii[i]), 0, sc->memmap[ASPEED_DEV_MII1 + i]); } @@ -469,7 +504,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); @@ -478,14 +513,14 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio_1_8v), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio_1_8v), 0, sc->memmap[ASPEED_DEV_GPIO_1_8V]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO_1_8V)); @@ -494,7 +529,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -503,7 +538,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->emmc), 0, + sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); @@ -511,7 +547,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -547,7 +583,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); @@ -555,7 +592,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[ASPEED_DEV_I3C] + i); @@ -567,7 +604,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); } static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 30574d4276a..b05b9dd4160 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" @@ -45,6 +46,7 @@ static const hwaddr aspeed_soc_ast2400_memmap[] = { [ASPEED_DEV_LPC] = 0x1E789000, [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, + [ASPEED_DEV_PECI] = 0x1E78B000, [ASPEED_DEV_ETH1] = 0x1E660000, [ASPEED_DEV_ETH2] = 0x1E680000, [ASPEED_DEV_UART1] = 0x1E783000, @@ -80,6 +82,7 @@ static const hwaddr aspeed_soc_ast2500_memmap[] = { [ASPEED_DEV_LPC] = 0x1E789000, [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, + [ASPEED_DEV_PECI] = 0x1E78B000, [ASPEED_DEV_ETH1] = 0x1E660000, [ASPEED_DEV_ETH2] = 0x1E680000, [ASPEED_DEV_UART1] = 0x1E783000, @@ -118,6 +121,7 @@ static const int aspeed_soc_ast2400_irqmap[] = { [ASPEED_DEV_PWM] = 28, [ASPEED_DEV_LPC] = 8, [ASPEED_DEV_I2C] = 12, + [ASPEED_DEV_PECI] = 15, [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, [ASPEED_DEV_XDMA] = 6, @@ -174,6 +178,8 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); @@ -191,8 +197,6 @@ static void aspeed_soc_init(Object *obj) object_initialize_child(obj, "sdmc", &s->sdmc, typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size"); - object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), - "max-ram-size"); for (i = 0; i < sc->wdts_num; i++) { snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); @@ -204,6 +208,10 @@ static void aspeed_soc_init(Object *obj) TYPE_FTGMAC100); } + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + snprintf(typename, sizeof(typename), TYPE_ASPEED_XDMA "-%s", socname); object_initialize_child(obj, "xdma", &s->xdma, typename); @@ -224,6 +232,9 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); object_initialize_child(obj, "hace", &s->hace, typename); + + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DEVICE); } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -232,43 +243,47 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); Error *err = NULL; + g_autofree char *sram_name = NULL; /* IO space */ - create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); /* Video engine stub */ - create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], - 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->video), "aspeed.video", + sc->memmap[ASPEED_DEV_VIDEO], 0x1000); /* CPU */ for (i = 0; i < sc->num_cpus; i++) { + 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 */ - memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", - sc->sram_size, &err); + 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(get_system_memory(), + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* VIC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->vic), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->memmap[ASPEED_DEV_VIC]); + 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, @@ -278,7 +293,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -288,7 +303,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -299,12 +314,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); /* UART */ - aspeed_soc_uart_init(s); + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -312,18 +329,27 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, aspeed_soc_get_irq(s, ASPEED_DEV_I2C)); + /* PECI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + sc->memmap[ASPEED_DEV_PECI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -333,9 +359,9 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -344,7 +370,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -354,7 +380,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + sc->memmap[ASPEED_DEV_SDMC]); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { @@ -365,10 +392,15 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); } + /* RAM */ + if (!aspeed_soc_dram_init(s, errp)) { + return; + } + /* Net */ for (i = 0; i < sc->macs_num; i++) { object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, @@ -376,7 +408,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -386,7 +418,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); @@ -395,7 +427,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); @@ -403,7 +436,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -412,7 +445,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the VIC */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -445,15 +478,16 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); } static Property aspeed_soc_properties[] = { + DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_UINT32("uart-default", AspeedSoCState, uart_default, - ASPEED_DEV_UART5), DEFINE_PROP_END_OF_LIST(), }; @@ -543,21 +577,93 @@ qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); } -void aspeed_soc_uart_init(AspeedSoCState *s) +bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) { AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - int i, uart; - - /* Attach an 8250 to the IO space as our UART */ - serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, - aspeed_soc_get_irq(s, s->uart_default), 38400, - serial_hd(0), DEVICE_LITTLE_ENDIAN); - for (i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { - if (uart == s->uart_default) { - uart++; + 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; } - serial_mm_init(get_system_memory(), sc->memmap[uart], 2, - aspeed_soc_get_irq(s, uart), 38400, - serial_hd(i), DEVICE_LITTLE_ENDIAN); + + 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/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 48538c9360c..3c2a4160cd1 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -23,6 +23,13 @@ /* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ #define BCM2835_SDHC_CAPAREG 0x52134b4 +/* + * According to Linux driver & DTS, dma channels 0--10 have separate IRQ, + * while channels 11--14 share one IRQ: + */ +#define SEPARATE_DMA_IRQ_MAX 10 +#define ORGATED_DMA_IRQ_COUNT 4 + static void create_unimp(BCM2835PeripheralState *ps, UnimplementedDeviceState *uds, const char *name, hwaddr ofs, hwaddr size) @@ -101,6 +108,11 @@ 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)); @@ -322,12 +334,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1)); - for (n = 0; n <= 12; n++) { + 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)) { diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c new file mode 100644 index 00000000000..79605f30646 --- /dev/null +++ b/hw/arm/fby35.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * This code is licensed under the GPL version 2 or later. See the COPYING + * file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" +#include "hw/boards.h" +#include "hw/qdev-clock.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/arm/boot.h" + +#define TYPE_FBY35 MACHINE_TYPE_NAME("fby35") +OBJECT_DECLARE_SIMPLE_TYPE(Fby35State, FBY35); + +struct Fby35State { + MachineState parent_obj; + + MemoryRegion bmc_memory; + MemoryRegion bmc_dram; + MemoryRegion bmc_boot_rom; + MemoryRegion bic_memory; + Clock *bic_sysclk; + + AspeedSoCState bmc; + AspeedSoCState bic; + + bool mmio_exec; +}; + +#define FBY35_BMC_RAM_SIZE (2 * GiB) +#define FBY35_BMC_FIRMWARE_ADDR 0x0 + +static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, + hwaddr offset, size_t rom_size, + Error **errp) +{ + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + g_autofree void *storage = NULL; + int64_t size; + + /* + * The block backend size should have already been 'validated' by + * the creation of the m25p80 object. + */ + size = blk_getlength(blk); + if (size <= 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + if (rom_size > size) { + rom_size = size; + } + + storage = g_malloc0(rom_size); + if (blk_pread(blk, 0, rom_size, storage, 0) < 0) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + + /* TODO: find a better way to install the ROM */ + memcpy(memory_region_get_ram_ptr(mr) + offset, storage, rom_size); +} + +static void fby35_bmc_init(Fby35State *s) +{ + DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); + + memory_region_init(&s->bmc_memory, OBJECT(s), "bmc-memory", UINT64_MAX); + memory_region_init_ram(&s->bmc_dram, OBJECT(s), "bmc-dram", + FBY35_BMC_RAM_SIZE, &error_abort); + + object_initialize_child(OBJECT(s), "bmc", &s->bmc, "ast2600-a3"); + object_property_set_int(OBJECT(&s->bmc), "ram-size", FBY35_BMC_RAM_SIZE, + &error_abort); + object_property_set_link(OBJECT(&s->bmc), "memory", OBJECT(&s->bmc_memory), + &error_abort); + object_property_set_link(OBJECT(&s->bmc), "dram", OBJECT(&s->bmc_dram), + &error_abort); + object_property_set_int(OBJECT(&s->bmc), "hw-strap1", 0x000000C0, + &error_abort); + object_property_set_int(OBJECT(&s->bmc), "hw-strap2", 0x00000003, + &error_abort); + aspeed_soc_uart_set_chr(&s->bmc, ASPEED_DEV_UART5, serial_hd(0)); + qdev_realize(DEVICE(&s->bmc), NULL, &error_abort); + + aspeed_board_init_flashes(&s->bmc.fmc, "n25q00", 2, 0); + + /* Install first FMC flash content as a boot rom. */ + if (drive0) { + AspeedSMCFlash *fl = &s->bmc.fmc.flashes[0]; + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + uint64_t size = memory_region_size(&fl->mmio); + + if (s->mmio_exec) { + memory_region_init_alias(boot_rom, NULL, "aspeed.boot_rom", + &fl->mmio, 0, size); + memory_region_add_subregion(&s->bmc_memory, FBY35_BMC_FIRMWARE_ADDR, + boot_rom); + } else { + + memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", + size, &error_abort); + memory_region_add_subregion(&s->bmc_memory, FBY35_BMC_FIRMWARE_ADDR, + boot_rom); + fby35_bmc_write_boot_rom(drive0, boot_rom, FBY35_BMC_FIRMWARE_ADDR, + size, &error_abort); + } + } +} + +static void fby35_bic_init(Fby35State *s) +{ + s->bic_sysclk = clock_new(OBJECT(s), "SYSCLK"); + clock_set_hz(s->bic_sysclk, 200000000ULL); + + memory_region_init(&s->bic_memory, OBJECT(s), "bic-memory", UINT64_MAX); + + object_initialize_child(OBJECT(s), "bic", &s->bic, "ast1030-a1"); + 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)); + 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); +} + +static void fby35_init(MachineState *machine) +{ + Fby35State *s = FBY35(machine); + + fby35_bmc_init(s); + fby35_bic_init(s); +} + + +static bool fby35_get_mmio_exec(Object *obj, Error **errp) +{ + return FBY35(obj)->mmio_exec; +} + +static void fby35_set_mmio_exec(Object *obj, bool value, Error **errp) +{ + FBY35(obj)->mmio_exec = value; +} + +static void fby35_instance_init(Object *obj) +{ + FBY35(obj)->mmio_exec = false; +} + +static void fby35_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Meta Platforms fby35"; + mc->init = fby35_init; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->min_cpus = mc->max_cpus = mc->default_cpus = 3; + + object_class_property_add_bool(oc, "execute-in-place", + fby35_get_mmio_exec, + fby35_set_mmio_exec); + object_class_property_set_description(oc, "execute-in-place", + "boot directly from CE0 flash device"); +} + +static const TypeInfo fby35_types[] = { + { + .name = MACHINE_TYPE_NAME("fby35"), + .parent = TYPE_MACHINE, + .class_init = fby35_class_init, + .instance_size = sizeof(Fby35State), + .instance_init = fby35_instance_init, + }, +}; + +DEFINE_TYPES(fby35_types); diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 2d8381339c0..92f9f6e000e 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -51,7 +51,8 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_soc.c', 'aspeed.c', 'aspeed_ast2600.c', - 'aspeed_ast10x0.c')) + 'aspeed_ast10x0.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')) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index f4f687df68e..93dda83d7aa 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1305,6 +1305,8 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) case I2C_NACK: s->status |= 1 << 1; /* set ACKNAK */ break; + default: + return -1; } pxa2xx_i2c_update(s); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 097238faa7a..9633f822f36 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -221,14 +221,18 @@ static bool cpu_type_valid(const char *cpu) return false; } -static void create_kaslr_seed(MachineState *ms, const char *node) +static void create_randomness(MachineState *ms, const char *node) { - uint64_t seed; + struct { + uint64_t kaslr; + uint8_t rng[32]; + } seed; if (qemu_guest_getrandom(&seed, sizeof(seed), NULL)) { return; } - qemu_fdt_setprop_u64(ms->fdt, node, "kaslr-seed", seed); + qemu_fdt_setprop_u64(ms->fdt, node, "kaslr-seed", seed.kaslr); + qemu_fdt_setprop(ms->fdt, node, "rng-seed", seed.rng, sizeof(seed.rng)); } static void create_fdt(VirtMachineState *vms) @@ -251,14 +255,14 @@ static void create_fdt(VirtMachineState *vms) /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); - if (vms->dtb_kaslr_seed) { - create_kaslr_seed(ms, "/chosen"); + if (vms->dtb_randomness) { + create_randomness(ms, "/chosen"); } if (vms->secure) { qemu_fdt_add_subnode(fdt, "/secure-chosen"); - if (vms->dtb_kaslr_seed) { - create_kaslr_seed(ms, "/secure-chosen"); + if (vms->dtb_randomness) { + create_randomness(ms, "/secure-chosen"); } } @@ -2010,15 +2014,7 @@ static void machvirt_init(MachineState *machine) cpuobj = object_new(possible_cpus->cpus[0].type); armcpu = ARM_CPU(cpuobj); - if (object_property_get_bool(cpuobj, "aarch64", NULL)) { - pa_bits = arm_pamax(armcpu); - } else if (arm_feature(&armcpu->env, ARM_FEATURE_LPAE)) { - /* v7 with LPAE */ - pa_bits = 40; - } else { - /* Anything else */ - pa_bits = 32; - } + pa_bits = arm_pamax(armcpu); object_unref(cpuobj); @@ -2348,18 +2344,18 @@ static void virt_set_its(Object *obj, bool value, Error **errp) vms->its = value; } -static bool virt_get_dtb_kaslr_seed(Object *obj, Error **errp) +static bool virt_get_dtb_randomness(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - return vms->dtb_kaslr_seed; + return vms->dtb_randomness; } -static void virt_set_dtb_kaslr_seed(Object *obj, bool value, Error **errp) +static void virt_set_dtb_randomness(Object *obj, bool value, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - vms->dtb_kaslr_seed = value; + vms->dtb_randomness = value; } static char *virt_get_oem_id(Object *obj, Error **errp) @@ -2988,12 +2984,18 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set on/off to enable/disable " "ITS instantiation"); + object_class_property_add_bool(oc, "dtb-randomness", + virt_get_dtb_randomness, + virt_set_dtb_randomness); + object_class_property_set_description(oc, "dtb-randomness", + "Set off to disable passing random or " + "non-deterministic dtb nodes to guest"); + object_class_property_add_bool(oc, "dtb-kaslr-seed", - virt_get_dtb_kaslr_seed, - virt_set_dtb_kaslr_seed); + virt_get_dtb_randomness, + virt_set_dtb_randomness); object_class_property_set_description(oc, "dtb-kaslr-seed", - "Set off to disable passing of kaslr-seed " - "dtb node to guest"); + "Deprecated synonym of dtb-randomness"); object_class_property_add_str(oc, "x-oem-id", virt_get_oem_id, @@ -3061,8 +3063,8 @@ static void virt_instance_init(Object *obj) /* MTE is disabled by default. */ vms->mte = false; - /* Supply a kaslr-seed by default */ - vms->dtb_kaslr_seed = true; + /* Supply kaslr-seed and rng-seed by default */ + vms->dtb_randomness = true; vms->irqmap = a15irqmap; diff --git a/hw/block/block.c b/hw/block/block.c index 25f45df7232..04279166eef 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -53,7 +53,7 @@ bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, * block device and read only on demand. */ assert(size <= BDRV_REQUEST_MAX_BYTES); - ret = blk_pread(blk, 0, buf, size); + ret = blk_pread(blk, 0, size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "can't read block backend"); return false; diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 57bb355794a..64ae4a6899d 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -1628,8 +1628,8 @@ int fdctrl_transfer_handler(void *opaque, int nchan, int dma_pos, int dma_len) if (fdctrl->data_dir != FD_DIR_WRITE || len < FD_SECTOR_LEN || rel_pos != 0) { /* READ & SCAN commands and realign to a sector for WRITE */ - if (blk_pread(cur_drv->blk, fd_offset(cur_drv), - fdctrl->fifo, BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("Floppy: error getting sector %d\n", fd_sector(cur_drv)); /* Sure, image size is too small... */ @@ -1656,8 +1656,8 @@ int fdctrl_transfer_handler(void *opaque, int nchan, int dma_pos, int dma_len) k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, fdctrl->data_pos, len); - if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), - fdctrl->fifo, BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); @@ -1740,8 +1740,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) fd_sector(cur_drv)); return 0; } - if (blk_pread(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, - BDRV_SECTOR_SIZE) + if (blk_pread(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error getting sector %d\n", fd_sector(cur_drv)); @@ -1820,8 +1820,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) } memset(fdctrl->fifo, 0, FD_SECTOR_LEN); if (cur_drv->blk == NULL || - blk_pwrite(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, - BDRV_SECTOR_SIZE, 0) < 0) { + blk_pwrite(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); } else { @@ -2244,8 +2244,8 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) if (pos == FD_SECTOR_LEN - 1 || fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); - if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); break; diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index dcbccee294c..112094358ee 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -63,7 +63,7 @@ static int guess_disk_lchs(BlockBackend *blk, blk_get_geometry(blk, &nb_sectors); - if (blk_pread(blk, 0, buf, BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(blk, 0, BDRV_SECTOR_SIZE, buf, 0) < 0) { return -1; } /* test msdos magic */ diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 81ba3da4df1..a8d2519141f 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -36,21 +36,19 @@ #include "trace.h" #include "qom/object.h" -/* Fields for FlashPartInfo->flags */ - -/* erase capabilities */ -#define ER_4K 1 -#define ER_32K 2 -/* set to allow the page program command to write 0s back to 1. Useful for - * modelling EEPROM with SPI flash command set - */ -#define EEPROM 0x100 - /* 16 MiB max in 3 byte address mode */ #define MAX_3BYTES_SIZE 0x1000000 - #define SPI_NOR_MAX_ID_LEN 6 +/* Fields for FlashPartInfo->flags */ +enum spi_flash_option_flags { + ER_4K = BIT(0), + ER_32K = BIT(1), + EEPROM = BIT(2), + HAS_SR_TB = BIT(3), + HAS_SR_BP3_BIT6 = BIT(4), +}; + typedef struct FlashPartInfo { const char *part_name; /* @@ -251,7 +249,8 @@ static const FlashPartInfo known_devices[] = { { INFO("n25q512a11", 0x20bb20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q512a13", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, - { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, + { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, + ER_4K | HAS_SR_BP3_BIT6 | HAS_SR_TB) }, { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q512ax3", 0x20ba20, 0x1000, 64 << 10, 1024, ER_4K) }, { INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) }, @@ -472,11 +471,18 @@ struct Flash { uint8_t spansion_cr2v; uint8_t spansion_cr3v; uint8_t spansion_cr4v; + bool wp_level; bool write_enable; bool four_bytes_address_mode; bool reset_enable; bool quad_enable; bool aai_enable; + bool block_protect0; + bool block_protect1; + bool block_protect2; + bool block_protect3; + bool top_bottom_bit; + bool status_register_write_disabled; uint8_t ear; int64_t dirty_page; @@ -621,12 +627,36 @@ void flash_write8(Flash *s, uint32_t addr, uint8_t data) { uint32_t page = addr / s->pi->page_size; uint8_t prev = s->storage[s->cur_addr]; + uint32_t block_protect_value = (s->block_protect3 << 3) | + (s->block_protect2 << 2) | + (s->block_protect1 << 1) | + (s->block_protect0 << 0); if (!s->write_enable) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n"); return; } + if (block_protect_value > 0) { + uint32_t num_protected_sectors = 1 << (block_protect_value - 1); + uint32_t sector = addr / s->pi->sector_size; + + /* top_bottom_bit == 0 means TOP */ + if (!s->top_bottom_bit) { + if (s->pi->n_sectors <= sector + num_protected_sectors) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: write with write protect!\n"); + return; + } + } else { + if (sector < num_protected_sectors) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: write with write protect!\n"); + return; + } + } + } + if ((prev ^ data) & data) { trace_m25p80_programming_zero_to_one(s, addr, prev, data); } @@ -723,6 +753,17 @@ static void complete_collecting_data(Flash *s) flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: + s->status_register_write_disabled = extract32(s->data[0], 7, 1); + s->block_protect0 = extract32(s->data[0], 2, 1); + s->block_protect1 = extract32(s->data[0], 3, 1); + s->block_protect2 = extract32(s->data[0], 4, 1); + if (s->pi->flags & HAS_SR_TB) { + s->top_bottom_bit = extract32(s->data[0], 5, 1); + } + if (s->pi->flags & HAS_SR_BP3_BIT6) { + s->block_protect3 = extract32(s->data[0], 6, 1); + } + switch (get_man(s)) { case MAN_SPANSION: s->quad_enable = !!(s->data[1] & 0x02); @@ -1165,22 +1206,34 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case WRSR: - if (s->write_enable) { - switch (get_man(s)) { - case MAN_SPANSION: - s->needed_bytes = 2; - s->state = STATE_COLLECTING_DATA; - break; - case MAN_MACRONIX: - s->needed_bytes = 2; - s->state = STATE_COLLECTING_VAR_LEN_DATA; - break; - default: - s->needed_bytes = 1; - s->state = STATE_COLLECTING_DATA; - } - s->pos = 0; + /* + * If WP# is low and status_register_write_disabled is high, + * status register writes are disabled. + * This is also called "hardware protected mode" (HPM). All other + * combinations of the two states are called "software protected mode" + * (SPM), and status register writes are permitted. + */ + if ((s->wp_level == 0 && s->status_register_write_disabled) + || !s->write_enable) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: Status register write is disabled!\n"); + break; } + + switch (get_man(s)) { + case MAN_SPANSION: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_DATA; + break; + case MAN_MACRONIX: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_VAR_LEN_DATA; + break; + default: + s->needed_bytes = 1; + s->state = STATE_COLLECTING_DATA; + } + s->pos = 0; break; case WRDI: @@ -1195,6 +1248,17 @@ static void decode_new_cmd(Flash *s, uint32_t value) case RDSR: s->data[0] = (!!s->write_enable) << 1; + s->data[0] |= (!!s->status_register_write_disabled) << 7; + s->data[0] |= (!!s->block_protect0) << 2; + s->data[0] |= (!!s->block_protect1) << 3; + s->data[0] |= (!!s->block_protect2) << 4; + if (s->pi->flags & HAS_SR_TB) { + s->data[0] |= (!!s->top_bottom_bit) << 5; + } + if (s->pi->flags & HAS_SR_BP3_BIT6) { + s->data[0] |= (!!s->block_protect3) << 6; + } + if (get_man(s) == MAN_MACRONIX || get_man(s) == MAN_ISSI) { s->data[0] |= (!!s->quad_enable) << 6; } @@ -1484,6 +1548,14 @@ static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx) return r; } +static void m25p80_write_protect_pin_irq_handler(void *opaque, int n, int level) +{ + Flash *s = M25P80(opaque); + /* WP# is just a single pin. */ + assert(n == 0); + s->wp_level = !!level; +} + static void m25p80_realize(SSIPeripheral *ss, Error **errp) { Flash *s = M25P80(ss); @@ -1506,7 +1578,7 @@ static void m25p80_realize(SSIPeripheral *ss, Error **errp) trace_m25p80_binding(s); s->storage = blk_blockalign(s->blk, s->size); - if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { + if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } @@ -1515,12 +1587,23 @@ static void m25p80_realize(SSIPeripheral *ss, Error **errp) s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } + + qdev_init_gpio_in_named(DEVICE(s), + m25p80_write_protect_pin_irq_handler, "WP#", 1); } static void m25p80_reset(DeviceState *d) { Flash *s = M25P80(d); + s->wp_level = true; + s->status_register_write_disabled = false; + s->block_protect0 = false; + s->block_protect1 = false; + s->block_protect2 = false; + s->block_protect3 = false; + s->top_bottom_bit = false; + reset_memory(s); } @@ -1587,6 +1670,51 @@ static const VMStateDescription vmstate_m25p80_aai_enable = { } }; +static bool m25p80_wp_level_srwd_needed(void *opaque) +{ + Flash *s = (Flash *)opaque; + + return !s->wp_level || s->status_register_write_disabled; +} + +static const VMStateDescription vmstate_m25p80_write_protect = { + .name = "m25p80/write_protect", + .version_id = 1, + .minimum_version_id = 1, + .needed = m25p80_wp_level_srwd_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(wp_level, Flash), + VMSTATE_BOOL(status_register_write_disabled, Flash), + VMSTATE_END_OF_LIST() + } +}; + +static bool m25p80_block_protect_needed(void *opaque) +{ + Flash *s = (Flash *)opaque; + + return s->block_protect0 || + s->block_protect1 || + s->block_protect2 || + s->block_protect3 || + s->top_bottom_bit; +} + +static const VMStateDescription vmstate_m25p80_block_protect = { + .name = "m25p80/block_protect", + .version_id = 1, + .minimum_version_id = 1, + .needed = m25p80_block_protect_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(block_protect0, Flash), + VMSTATE_BOOL(block_protect1, Flash), + VMSTATE_BOOL(block_protect2, Flash), + VMSTATE_BOOL(block_protect3, Flash), + VMSTATE_BOOL(top_bottom_bit, Flash), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_m25p80 = { .name = "m25p80", .version_id = 0, @@ -1618,6 +1746,8 @@ static const VMStateDescription vmstate_m25p80 = { .subsections = (const VMStateDescription * []) { &vmstate_m25p80_data_read_loop, &vmstate_m25p80_aai_enable, + &vmstate_m25p80_write_protect, + &vmstate_m25p80_block_protect, NULL } }; diff --git a/hw/block/nand.c b/hw/block/nand.c index 8bc80e35144..1aee1cb2b1c 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -666,8 +666,8 @@ static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s) sector = SECTOR(s->addr); off = (s->addr & PAGE_MASK) + s->offset; soff = SECTOR_OFFSET(s->addr); - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - PAGE_SECTORS << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, + PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } @@ -679,24 +679,24 @@ static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s) MIN(OOB_SIZE, off + s->iolen - NAND_PAGE_SIZE)); } - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - PAGE_SECTORS << BDRV_SECTOR_BITS, 0) < 0) { + if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, + PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } } else { off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; sector = off >> 9; soff = off & 0x1ff; - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } mem_and(iobuf + soff, s->io, s->iolen); - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, 0) < 0) { + if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } } @@ -723,20 +723,20 @@ static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) i = SECTOR(addr); page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); for (; i < page; i ++) - if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i); } } else { addr = PAGE_START(addr); page = addr >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, page); } memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } @@ -744,20 +744,20 @@ static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) i = (addr & ~0x1ff) + 0x200; for (addr += ((NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; i < addr; i += 0x200) { - if (blk_pwrite(s->blk, i, iobuf, BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, i, BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i >> 9); } } page = i >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, page); } memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } } @@ -772,8 +772,8 @@ static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, if (s->blk) { if (s->mem_oob) { - if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, s->io, - PAGE_SECTORS << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, + PAGE_SECTORS << BDRV_SECTOR_BITS, s->io, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, SECTOR(addr)); } @@ -782,8 +782,9 @@ static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, OOB_SIZE); s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; } else { - if (blk_pread(s->blk, PAGE_START(addr), s->io, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, PAGE_START(addr), + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, s->io, 0) + < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, PAGE_START(addr) >> 9); } diff --git a/hw/block/onenand.c b/hw/block/onenand.c index afc0cd3a0ff..1fde9750249 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -229,8 +229,8 @@ static void onenand_reset(OneNANDState *s, int cold) /* Lock the whole flash */ memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); - if (s->blk_cur && blk_pread(s->blk_cur, 0, s->boot[0], - 8 << BDRV_SECTOR_BITS) < 0) { + if (s->blk_cur && blk_pread(s->blk_cur, 0, 8 << BDRV_SECTOR_BITS, + s->boot[0], 0) < 0) { hw_error("%s: Loading the BootRAM failed.\n", __func__); } } @@ -249,8 +249,8 @@ static inline int onenand_load_main(OneNANDState *s, int sec, int secn, assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec); assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn); if (s->blk_cur) { - return blk_pread(s->blk_cur, sec << BDRV_SECTOR_BITS, dest, - secn << BDRV_SECTOR_BITS) < 0; + return blk_pread(s->blk_cur, sec << BDRV_SECTOR_BITS, + secn << BDRV_SECTOR_BITS, dest, 0) < 0; } else if (sec + secn > s->secs_cur) { return 1; } @@ -274,7 +274,7 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, uint8_t *dp = 0; if (s->blk_cur) { dp = g_malloc(size); - if (!dp || blk_pread(s->blk_cur, offset, dp, size) < 0) { + if (!dp || blk_pread(s->blk_cur, offset, size, dp, 0) < 0) { result = 1; } } else { @@ -290,7 +290,7 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, dp[i] &= sp[i]; } if (s->blk_cur) { - result = blk_pwrite(s->blk_cur, offset, dp, size, 0) < 0; + result = blk_pwrite(s->blk_cur, offset, size, dp, 0) < 0; } } if (dp && s->blk_cur) { @@ -308,7 +308,7 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, if (s->blk_cur) { uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS; - if (blk_pread(s->blk_cur, offset, buf, BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk_cur, offset, BDRV_SECTOR_SIZE, buf, 0) < 0) { return 1; } memcpy(dest, buf + ((sec & 31) << 4), secn << 4); @@ -333,7 +333,7 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, if (s->blk_cur) { dp = g_malloc(512); if (!dp - || blk_pread(s->blk_cur, offset, dp, BDRV_SECTOR_SIZE) < 0) { + || blk_pread(s->blk_cur, offset, BDRV_SECTOR_SIZE, dp, 0) < 0) { result = 1; } else { dpp = dp + ((sec & 31) << 4); @@ -351,8 +351,8 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, dpp[i] &= sp[i]; } if (s->blk_cur) { - result = blk_pwrite(s->blk_cur, offset, dp, - BDRV_SECTOR_SIZE, 0) < 0; + result = blk_pwrite(s->blk_cur, offset, BDRV_SECTOR_SIZE, dp, + 0) < 0; } } g_free(dp); @@ -370,17 +370,17 @@ static inline int onenand_erase(OneNANDState *s, int sec, int num) for (; num > 0; num--, sec++) { if (s->blk_cur) { int erasesec = s->secs_cur + (sec >> 5); - if (blk_pwrite(s->blk_cur, sec << BDRV_SECTOR_BITS, blankbuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk_cur, sec << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, blankbuf, 0) < 0) { goto fail; } - if (blk_pread(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf, - BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk_cur, erasesec << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, tmpbuf, 0) < 0) { goto fail; } memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); - if (blk_pwrite(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk_cur, erasesec << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, tmpbuf, 0) < 0) { goto fail; } } else { diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 74c7190302b..0cbc2fb4cbf 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -392,8 +392,8 @@ static void pflash_update(PFlashCFI01 *pfl, int offset, /* widen to sector boundaries */ offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); - ret = blk_pwrite(pfl->blk, offset, pfl->storage + offset, - offset_end - offset, 0); + ret = blk_pwrite(pfl->blk, offset, offset_end - offset, + pfl->storage + offset, 0); if (ret < 0) { /* TODO set error bit in status */ error_report("Could not update PFLASH: %s", strerror(-ret)); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 02c514fb6e0..2a99b286b07 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -400,8 +400,8 @@ static void pflash_update(PFlashCFI02 *pfl, int offset, int size) /* widen to sector boundaries */ offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); - ret = blk_pwrite(pfl->blk, offset, pfl->storage + offset, - offset_end - offset, 0); + ret = blk_pwrite(pfl->blk, offset, offset_end - offset, + pfl->storage + offset, 0); if (ret < 0) { /* TODO set error bit in status */ error_report("Could not update PFLASH: %s", strerror(-ret)); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index cd804795c60..e9ba752f6bf 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1228,7 +1228,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); blk_set_dev_ops(s->blk, &virtio_block_ops, s); - blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size); blk_iostatus_enable(s->blk); diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 674953f1ade..345b284d701 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -243,7 +243,6 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) } blk_set_dev_ops(blk, &xen_block_dev_ops, blockdev); - blk_set_guest_block_size(blk, conf->logical_block_size); if (conf->discard_granularity == -1) { conf->discard_granularity = conf->physical_block_size; diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index efa14908d8c..483d8eb13f6 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -129,8 +129,9 @@ static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr, static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) { - CXLComponentState *hb_cstate; + CXLComponentState *hb_cstate, *usp_cstate; PCIHostState *hb; + CXLUpstreamPort *usp; int rb_index; uint32_t *cache_mem; uint8_t target; @@ -164,8 +165,46 @@ static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) } d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0]; + if (!d) { + return NULL; + } + + if (object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { + return d; + } + + /* + * Could also be a switch. Note only one level of switching currently + * supported. + */ + if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_USP)) { + return NULL; + } + usp = CXL_USP(d); + + usp_cstate = cxl_usp_to_cstate(usp); + if (!usp_cstate) { + return NULL; + } + + cache_mem = usp_cstate->crb.cache_mem_registers; + + target_found = cxl_hdm_find_target(cache_mem, addr, &target); + if (!target_found) { + return NULL; + } + + d = pcie_find_port_by_pn(&PCI_BRIDGE(d)->sec_bus, target); + if (!d) { + return NULL; + } + + d = pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devices[0]; + if (!d) { + return NULL; + } - if (!d || !object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { + if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { return NULL; } diff --git a/hw/display/artist.c b/hw/display/artist.c index eadaef0d466..fde050c882b 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -1358,7 +1358,7 @@ static void artist_create_buffer(ARTISTState *s, const char *name, { struct vram_buffer *buf = s->vram_buffer + idx; - memory_region_init_ram(&buf->mr, NULL, name, width * height, + memory_region_init_ram(&buf->mr, OBJECT(s), name, width * height, &error_fatal); memory_region_add_subregion_overlap(&s->mem_as_root, *offset, &buf->mr, 0); diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c index b591a587890..664fd4046d8 100644 --- a/hw/display/sii9022.c +++ b/hw/display/sii9022.c @@ -76,6 +76,8 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event) break; case I2C_NACK: break; + default: + return -1; } return 0; diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index aeae22da9c2..d67b0ad7b52 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -196,6 +196,8 @@ static int ssd0303_event(I2CSlave *i2c, enum i2c_event event) case I2C_NACK: /* Nothing to do. */ break; + default: + return -1; } return 0; diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index a62a673857c..1e267dd4820 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -268,7 +268,7 @@ static ptrdiff_t aspeed_gpio_set_idx(AspeedGPIOState *s, GPIOSets *regs) } static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, - uint32_t value) + uint32_t value, uint32_t mode_mask) { uint32_t input_mask = regs->input_mask; uint32_t direction = regs->direction; @@ -277,7 +277,8 @@ static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, uint32_t diff; int gpio; - diff = old ^ new; + diff = (old ^ new); + diff &= mode_mask; if (diff) { for (gpio = 0; gpio < ASPEED_GPIOS_PER_SET; gpio++) { uint32_t mask = 1 << gpio; @@ -339,7 +340,7 @@ static void aspeed_gpio_set_pin_level(AspeedGPIOState *s, uint32_t set_idx, value &= ~pin_mask; } - aspeed_gpio_update(s, &s->sets[set_idx], value); + aspeed_gpio_update(s, &s->sets[set_idx], value, ~s->sets[set_idx].direction); } /* @@ -653,7 +654,7 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, reg_value = update_value_control_source(set, set->data_value, reg_value); set->data_read = reg_value; - aspeed_gpio_update(s, set, reg_value); + aspeed_gpio_update(s, set, reg_value, set->direction); return; case gpio_reg_idx_direction: reg_value = set->direction; @@ -753,7 +754,7 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, __func__, offset, data, reg_idx_type); return; } - aspeed_gpio_update(s, set, set->data_value); + aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); return; } @@ -799,7 +800,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, data &= props->output; data = update_value_control_source(set, set->data_value, data); set->data_read = data; - aspeed_gpio_update(s, set, data); + aspeed_gpio_update(s, set, data, set->direction); return; case gpio_reg_direction: /* @@ -875,7 +876,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, PRIx64"\n", __func__, offset); return; } - aspeed_gpio_update(s, set, set->data_value); + aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); return; } diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 63b9dd2396c..e53d5f0fa74 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -280,8 +280,16 @@ static void machine_hppa_init(MachineState *machine) } /* PS/2 Keyboard/Mouse */ - lasips2_init(addr_space, LASI_PS2KBD_HPA, - qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA)); + 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)); /* register power switch emulation */ qemu_register_powerdown_notifier(&hppa_system_powerdown_notifier); diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 03a4f5a9101..42c6d69b82f 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "migration/vmstate.h" +#include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/error-report.h" @@ -28,196 +29,98 @@ #include "hw/i2c/aspeed_i2c.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/registerfields.h" #include "trace.h" -/* I2C Global Register */ - -#define I2C_CTRL_STATUS 0x00 /* Device Interrupt Status */ -#define I2C_CTRL_ASSIGN 0x08 /* Device Interrupt Target - Assignment */ -#define I2C_CTRL_GLOBAL 0x0C /* Global Control Register */ -#define I2C_CTRL_SRAM_EN BIT(0) - -/* I2C Device (Bus) Register */ - -#define I2CD_FUN_CTRL_REG 0x00 /* I2CD Function Control */ -#define I2CD_POOL_PAGE_SEL(x) (((x) >> 20) & 0x7) /* AST2400 */ -#define I2CD_M_SDA_LOCK_EN (0x1 << 16) -#define I2CD_MULTI_MASTER_DIS (0x1 << 15) -#define I2CD_M_SCL_DRIVE_EN (0x1 << 14) -#define I2CD_MSB_STS (0x1 << 9) -#define I2CD_SDA_DRIVE_1T_EN (0x1 << 8) -#define I2CD_M_SDA_DRIVE_1T_EN (0x1 << 7) -#define I2CD_M_HIGH_SPEED_EN (0x1 << 6) -#define I2CD_DEF_ADDR_EN (0x1 << 5) -#define I2CD_DEF_ALERT_EN (0x1 << 4) -#define I2CD_DEF_ARP_EN (0x1 << 3) -#define I2CD_DEF_GCALL_EN (0x1 << 2) -#define I2CD_SLAVE_EN (0x1 << 1) -#define I2CD_MASTER_EN (0x1) - -#define I2CD_AC_TIMING_REG1 0x04 /* Clock and AC Timing Control #1 */ -#define I2CD_AC_TIMING_REG2 0x08 /* Clock and AC Timing Control #1 */ -#define I2CD_INTR_CTRL_REG 0x0c /* I2CD Interrupt Control */ -#define I2CD_INTR_STS_REG 0x10 /* I2CD Interrupt Status */ - -#define I2CD_INTR_SLAVE_ADDR_MATCH (0x1 << 31) /* 0: addr1 1: addr2 */ -#define I2CD_INTR_SLAVE_ADDR_RX_PENDING (0x1 << 30) -/* bits[19-16] Reserved */ - -/* All bits below are cleared by writing 1 */ -#define I2CD_INTR_SLAVE_INACTIVE_TIMEOUT (0x1 << 15) -#define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14) -#define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13) -#define I2CD_INTR_SMBUS_ALERT (0x1 << 12) /* Bus [0-3] only */ -#define I2CD_INTR_SMBUS_ARP_ADDR (0x1 << 11) /* Removed */ -#define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10) /* Removed */ -#define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9) /* Removed */ -#define I2CD_INTR_GCALL_ADDR (0x1 << 8) /* Removed */ -#define I2CD_INTR_SLAVE_ADDR_RX_MATCH (0x1 << 7) /* use RX_DONE */ -#define I2CD_INTR_SCL_TIMEOUT (0x1 << 6) -#define I2CD_INTR_ABNORMAL (0x1 << 5) -#define I2CD_INTR_NORMAL_STOP (0x1 << 4) -#define I2CD_INTR_ARBIT_LOSS (0x1 << 3) -#define I2CD_INTR_RX_DONE (0x1 << 2) -#define I2CD_INTR_TX_NAK (0x1 << 1) -#define I2CD_INTR_TX_ACK (0x1 << 0) - -#define I2CD_CMD_REG 0x14 /* I2CD Command/Status */ -#define I2CD_SDA_OE (0x1 << 28) -#define I2CD_SDA_O (0x1 << 27) -#define I2CD_SCL_OE (0x1 << 26) -#define I2CD_SCL_O (0x1 << 25) -#define I2CD_TX_TIMING (0x1 << 24) -#define I2CD_TX_STATUS (0x1 << 23) - -#define I2CD_TX_STATE_SHIFT 19 /* Tx State Machine */ -#define I2CD_TX_STATE_MASK 0xf -#define I2CD_IDLE 0x0 -#define I2CD_MACTIVE 0x8 -#define I2CD_MSTART 0x9 -#define I2CD_MSTARTR 0xa -#define I2CD_MSTOP 0xb -#define I2CD_MTXD 0xc -#define I2CD_MRXACK 0xd -#define I2CD_MRXD 0xe -#define I2CD_MTXACK 0xf -#define I2CD_SWAIT 0x1 -#define I2CD_SRXD 0x4 -#define I2CD_STXACK 0x5 -#define I2CD_STXD 0x6 -#define I2CD_SRXACK 0x7 -#define I2CD_RECOVER 0x3 - -#define I2CD_SCL_LINE_STS (0x1 << 18) -#define I2CD_SDA_LINE_STS (0x1 << 17) -#define I2CD_BUS_BUSY_STS (0x1 << 16) -#define I2CD_SDA_OE_OUT_DIR (0x1 << 15) -#define I2CD_SDA_O_OUT_DIR (0x1 << 14) -#define I2CD_SCL_OE_OUT_DIR (0x1 << 13) -#define I2CD_SCL_O_OUT_DIR (0x1 << 12) -#define I2CD_BUS_RECOVER_CMD_EN (0x1 << 11) -#define I2CD_S_ALT_EN (0x1 << 10) - -/* Command Bit */ -#define I2CD_RX_DMA_ENABLE (0x1 << 9) -#define I2CD_TX_DMA_ENABLE (0x1 << 8) -#define I2CD_RX_BUFF_ENABLE (0x1 << 7) -#define I2CD_TX_BUFF_ENABLE (0x1 << 6) -#define I2CD_M_STOP_CMD (0x1 << 5) -#define I2CD_M_S_RX_CMD_LAST (0x1 << 4) -#define I2CD_M_RX_CMD (0x1 << 3) -#define I2CD_S_TX_CMD (0x1 << 2) -#define I2CD_M_TX_CMD (0x1 << 1) -#define I2CD_M_START_CMD (0x1) - -#define I2CD_DEV_ADDR_REG 0x18 /* Slave Device Address */ -#define I2CD_POOL_CTRL_REG 0x1c /* Pool Buffer Control */ -#define I2CD_POOL_RX_COUNT(x) (((x) >> 24) & 0xff) -#define I2CD_POOL_RX_SIZE(x) ((((x) >> 16) & 0xff) + 1) -#define I2CD_POOL_TX_COUNT(x) ((((x) >> 8) & 0xff) + 1) -#define I2CD_POOL_OFFSET(x) (((x) & 0x3f) << 2) /* AST2400 */ -#define I2CD_BYTE_BUF_REG 0x20 /* Transmit/Receive Byte Buffer */ -#define I2CD_BYTE_BUF_TX_SHIFT 0 -#define I2CD_BYTE_BUF_TX_MASK 0xff -#define I2CD_BYTE_BUF_RX_SHIFT 8 -#define I2CD_BYTE_BUF_RX_MASK 0xff -#define I2CD_DMA_ADDR 0x24 /* DMA Buffer Address */ -#define I2CD_DMA_LEN 0x28 /* DMA Transfer Length < 4KB */ - -static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus) -{ - return bus->ctrl & I2CD_MASTER_EN; -} - -static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) -{ - return bus->ctrl & (I2CD_MASTER_EN | I2CD_SLAVE_EN); -} +/* Enable SLAVE_ADDR_RX_MATCH always */ +#define R_I2CD_INTR_STS_ALWAYS_ENABLE R_I2CD_INTR_STS_SLAVE_ADDR_RX_MATCH_MASK static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t intr_ctrl_reg = aspeed_i2c_bus_intr_ctrl_offset(bus); + uint32_t intr_ctrl_mask = bus->regs[intr_ctrl_reg] | + R_I2CD_INTR_STS_ALWAYS_ENABLE; + bool raise_irq; + + if (trace_event_get_state_backends(TRACE_ASPEED_I2C_BUS_RAISE_INTERRUPT)) { + g_autofree char *buf = g_strdup_printf("%s%s%s%s%s%s%s", + aspeed_i2c_bus_pkt_mode_en(bus) && + ARRAY_FIELD_EX32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE) ? + "pktdone|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_NAK) ? + "nak|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_ACK) ? + "ack|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE) ? + "done|" : "", + ARRAY_FIELD_EX32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH) ? + "slave-match|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, NORMAL_STOP) ? + "stop|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, ABNORMAL) ? + "abnormal" : ""); + + trace_aspeed_i2c_bus_raise_interrupt(bus->regs[reg_intr_sts], buf); + } - trace_aspeed_i2c_bus_raise_interrupt(bus->intr_status, - bus->intr_status & I2CD_INTR_TX_NAK ? "nak|" : "", - bus->intr_status & I2CD_INTR_TX_ACK ? "ack|" : "", - bus->intr_status & I2CD_INTR_RX_DONE ? "done|" : "", - bus->intr_status & I2CD_INTR_NORMAL_STOP ? "normal|" : "", - bus->intr_status & I2CD_INTR_ABNORMAL ? "abnormal" : ""); + raise_irq = bus->regs[reg_intr_sts] & intr_ctrl_mask ; + + /* In packet mode we don't mask off INTR_STS */ + if (!aspeed_i2c_bus_pkt_mode_en(bus)) { + bus->regs[reg_intr_sts] &= intr_ctrl_mask; + } - bus->intr_status &= bus->intr_ctrl; - if (bus->intr_status) { + if (raise_irq) { bus->controller->intr_status |= 1 << bus->id; qemu_irq_raise(aic->bus_get_irq(bus)); } } -static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, - unsigned size) +static inline void aspeed_i2c_bus_raise_slave_interrupt(AspeedI2CBus *bus) { - AspeedI2CBus *bus = opaque; AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); - uint64_t value = -1; + + if (!bus->regs[R_I2CS_INTR_STS]) { + return; + } + + bus->controller->intr_status |= 1 << bus->id; + qemu_irq_raise(aic->bus_get_irq(bus)); +} + +static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, + unsigned size) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; switch (offset) { - case I2CD_FUN_CTRL_REG: - value = bus->ctrl; - break; - case I2CD_AC_TIMING_REG1: - value = bus->timing[0]; - break; - case I2CD_AC_TIMING_REG2: - value = bus->timing[1]; - break; - case I2CD_INTR_CTRL_REG: - value = bus->intr_ctrl; - break; - case I2CD_INTR_STS_REG: - value = bus->intr_status; - break; - case I2CD_POOL_CTRL_REG: - value = bus->pool_ctrl; + case A_I2CD_FUN_CTRL: + case A_I2CD_AC_TIMING1: + case A_I2CD_AC_TIMING2: + case A_I2CD_INTR_CTRL: + case A_I2CD_INTR_STS: + case A_I2CD_DEV_ADDR: + case A_I2CD_POOL_CTRL: + case A_I2CD_BYTE_BUF: + /* Value is already set, don't do anything. */ break; - case I2CD_BYTE_BUF_REG: - value = bus->buf; + case A_I2CD_CMD: + value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); break; - case I2CD_CMD_REG: - value = bus->cmd | (i2c_bus_busy(bus->bus) << 16); - break; - case I2CD_DMA_ADDR: + case A_I2CD_DMA_ADDR: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); - break; + value = -1; } - value = bus->dma_addr; break; - case I2CD_DMA_LEN: + case A_I2CD_DMA_LEN: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); - break; + value = -1; } - value = bus->dma_len; break; default: @@ -231,32 +134,95 @@ static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, return value; } +static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, + unsigned size) +{ + uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; + + switch (offset) { + case A_I2CC_FUN_CTRL: + case A_I2CC_AC_TIMING: + case A_I2CC_POOL_CTRL: + case A_I2CM_INTR_CTRL: + case A_I2CM_INTR_STS: + case A_I2CC_MS_TXRX_BYTE_BUF: + case A_I2CM_DMA_LEN: + case A_I2CM_DMA_TX_ADDR: + case A_I2CM_DMA_RX_ADDR: + case A_I2CM_DMA_LEN_STS: + case A_I2CC_DMA_ADDR: + case A_I2CC_DMA_LEN: + + case A_I2CS_DEV_ADDR: + case A_I2CS_DMA_RX_ADDR: + case A_I2CS_DMA_LEN: + case A_I2CS_CMD: + case A_I2CS_INTR_CTRL: + case A_I2CS_DMA_LEN_STS: + /* Value is already set, don't do anything. */ + break; + case A_I2CS_INTR_STS: + break; + case A_I2CM_CMD: + value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); + value = -1; + break; + } + + trace_aspeed_i2c_bus_read(bus->id, offset, size, value); + return value; +} + +static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedI2CBus *bus = opaque; + if (aspeed_i2c_is_new_mode(bus->controller)) { + return aspeed_i2c_bus_new_read(bus, offset, size); + } + return aspeed_i2c_bus_old_read(bus, offset, size); +} + static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) { - bus->cmd &= ~(I2CD_TX_STATE_MASK << I2CD_TX_STATE_SHIFT); - bus->cmd |= (state & I2CD_TX_STATE_MASK) << I2CD_TX_STATE_SHIFT; + if (aspeed_i2c_is_new_mode(bus->controller)) { + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_STATE, + state); + } else { + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_CMD, TX_STATE, state); + } } static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) { - return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; + if (aspeed_i2c_is_new_mode(bus->controller)) { + return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, + TX_STATE); + } + return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, TX_STATE); } static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) { MemTxResult result; AspeedI2CState *s = bus->controller; + uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); - result = address_space_read(&s->dram_as, bus->dma_addr, + result = address_space_read(&s->dram_as, bus->regs[reg_dma_addr], MEMTXATTRS_UNSPECIFIED, data, 1); if (result != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n", - __func__, bus->dma_addr); + __func__, bus->regs[reg_dma_addr]); return -1; } - bus->dma_addr++; - bus->dma_len--; + bus->regs[reg_dma_addr]++; + bus->regs[reg_dma_len]--; return 0; } @@ -265,34 +231,51 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); int ret = -1; int i; - - if (bus->cmd & I2CD_TX_BUFF_ENABLE) { - for (i = pool_start; i < I2CD_POOL_TX_COUNT(bus->pool_ctrl); i++) { + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, + TX_COUNT); + + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { + for (i = pool_start; i < pool_tx_count; i++) { uint8_t *pool_base = aic->bus_pool_base(bus); - trace_aspeed_i2c_bus_send("BUF", i + 1, - I2CD_POOL_TX_COUNT(bus->pool_ctrl), + trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, pool_base[i]); ret = i2c_send(bus->bus, pool_base[i]); if (ret) { break; } } - bus->cmd &= ~I2CD_TX_BUFF_ENABLE; - } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { - while (bus->dma_len) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0); + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + /* In new mode, clear how many bytes we TXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); + } + while (bus->regs[reg_dma_len]) { uint8_t data; aspeed_i2c_dma_read(bus, &data); - trace_aspeed_i2c_bus_send("DMA", bus->dma_len, bus->dma_len, data); + trace_aspeed_i2c_bus_send("DMA", bus->regs[reg_dma_len], + bus->regs[reg_dma_len], data); ret = i2c_send(bus->bus, data); if (ret) { break; } + /* In new mode, keep track of how many bytes we TXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, + ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, + TX_LEN) + 1); + } } - bus->cmd &= ~I2CD_TX_DMA_ENABLE; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); } else { - trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, bus->buf); - ret = i2c_send(bus->bus, bus->buf); + trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, + bus->regs[reg_byte_buf]); + ret = i2c_send(bus->bus, bus->regs[reg_byte_buf]); } return ret; @@ -304,74 +287,100 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); uint8_t data; int i; - - if (bus->cmd & I2CD_RX_BUFF_ENABLE) { + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); + int pool_rx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, + RX_COUNT); + + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { uint8_t *pool_base = aic->bus_pool_base(bus); - for (i = 0; i < I2CD_POOL_RX_SIZE(bus->pool_ctrl); i++) { + for (i = 0; i < pool_rx_count; i++) { pool_base[i] = i2c_recv(bus->bus); - trace_aspeed_i2c_bus_recv("BUF", i + 1, - I2CD_POOL_RX_SIZE(bus->pool_ctrl), + trace_aspeed_i2c_bus_recv("BUF", i + 1, pool_rx_count, pool_base[i]); } /* Update RX count */ - bus->pool_ctrl &= ~(0xff << 24); - bus->pool_ctrl |= (i & 0xff) << 24; - bus->cmd &= ~I2CD_RX_BUFF_ENABLE; - } else if (bus->cmd & I2CD_RX_DMA_ENABLE) { + 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); + } - while (bus->dma_len) { + while (bus->regs[reg_dma_len]) { MemTxResult result; data = i2c_recv(bus->bus); - trace_aspeed_i2c_bus_recv("DMA", bus->dma_len, bus->dma_len, data); - result = address_space_write(&s->dram_as, bus->dma_addr, + trace_aspeed_i2c_bus_recv("DMA", bus->regs[reg_dma_len], + bus->regs[reg_dma_len], data); + result = address_space_write(&s->dram_as, bus->regs[reg_dma_addr], MEMTXATTRS_UNSPECIFIED, &data, 1); if (result != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n", - __func__, bus->dma_addr); + __func__, bus->regs[reg_dma_addr]); return; } - bus->dma_addr++; - bus->dma_len--; + bus->regs[reg_dma_addr]++; + bus->regs[reg_dma_len]--; + /* In new mode, keep track of how many bytes we RXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, + ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, + RX_LEN) + 1); + } } - bus->cmd &= ~I2CD_RX_DMA_ENABLE; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); } else { data = i2c_recv(bus->bus); - trace_aspeed_i2c_bus_recv("BYTE", 1, 1, bus->buf); - bus->buf = (data & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; + trace_aspeed_i2c_bus_recv("BYTE", 1, 1, bus->regs[reg_byte_buf]); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); } } static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) { + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + aspeed_i2c_set_state(bus, I2CD_MRXD); aspeed_i2c_bus_recv(bus); - bus->intr_status |= I2CD_INTR_RX_DONE; - if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) { i2c_nack(bus->bus); } - bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_RX_CMD, 0); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_S_RX_CMD_LAST, 0); aspeed_i2c_set_state(bus, I2CD_MACTIVE); } static uint8_t aspeed_i2c_get_addr(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); - if (bus->cmd & I2CD_TX_BUFF_ENABLE) { + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + return (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, PKT_DEV_ADDR) << 1) | + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD); + } + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { uint8_t *pool_base = aic->bus_pool_base(bus); return pool_base[0]; - } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { uint8_t data; aspeed_i2c_dma_read(bus, &data); return data; } else { - return bus->buf; + return bus->regs[reg_byte_buf]; } } @@ -379,7 +388,11 @@ static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) { AspeedI2CState *s = bus->controller; AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); - + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + bool dma_en = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN); if (!aic->check_sram) { return true; } @@ -388,9 +401,7 @@ static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) * AST2500: SRAM must be enabled before using the Buffer Pool or * DMA mode. */ - if (!(s->ctrl_global & I2C_CTRL_SRAM_EN) && - (bus->cmd & (I2CD_RX_DMA_ENABLE | I2CD_TX_DMA_ENABLE | - I2CD_RX_BUFF_ENABLE | I2CD_TX_BUFF_ENABLE))) { + if (!FIELD_EX32(s->ctrl_global, I2C_CTRL_GLOBAL, SRAM_EN) && dma_en) { qemu_log_mask(LOG_GUEST_ERROR, "%s: SRAM is not enabled\n", __func__); return false; } @@ -402,27 +413,31 @@ static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) { g_autofree char *cmd_flags = NULL; uint32_t count; - - if (bus->cmd & (I2CD_RX_BUFF_ENABLE | I2CD_RX_BUFF_ENABLE)) { - count = I2CD_POOL_TX_COUNT(bus->pool_ctrl); - } else if (bus->cmd & (I2CD_RX_DMA_ENABLE | I2CD_RX_DMA_ENABLE)) { - count = bus->dma_len; + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { + count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT); + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { + count = bus->regs[reg_dma_len]; } else { /* BYTE mode */ count = 1; } cmd_flags = g_strdup_printf("%s%s%s%s%s%s%s%s%s", - bus->cmd & I2CD_M_START_CMD ? "start|" : "", - bus->cmd & I2CD_RX_DMA_ENABLE ? "rxdma|" : "", - bus->cmd & I2CD_TX_DMA_ENABLE ? "txdma|" : "", - bus->cmd & I2CD_RX_BUFF_ENABLE ? "rxbuf|" : "", - bus->cmd & I2CD_TX_BUFF_ENABLE ? "txbuf|" : "", - bus->cmd & I2CD_M_TX_CMD ? "tx|" : "", - bus->cmd & I2CD_M_RX_CMD ? "rx|" : "", - bus->cmd & I2CD_M_S_RX_CMD_LAST ? "last|" : "", - bus->cmd & I2CD_M_STOP_CMD ? "stop" : ""); - - trace_aspeed_i2c_bus_cmd(bus->cmd, cmd_flags, count, bus->intr_status); + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD) ? "start|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) ? "rxdma|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) ? "txdma|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) ? "rxbuf|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN) ? "txbuf|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD) ? "tx|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) ? "rx|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST) ? "last|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD) ? "stop|" : ""); + + trace_aspeed_i2c_bus_cmd(bus->regs[reg_cmd], cmd_flags, count, + bus->regs[reg_intr_sts]); } /* @@ -432,9 +447,10 @@ static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { uint8_t pool_start = 0; - - bus->cmd &= ~0xFFFF; - bus->cmd |= value & 0xFFFF; + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); if (!aspeed_i2c_check_sram(bus)) { return; @@ -444,7 +460,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) aspeed_i2c_bus_cmd_dump(bus); } - if (bus->cmd & I2CD_M_START_CMD) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) { uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? I2CD_MSTARTR : I2CD_MSTART; uint8_t addr; @@ -452,24 +468,30 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) aspeed_i2c_set_state(bus, state); addr = aspeed_i2c_get_addr(bus); - if (i2c_start_transfer(bus->bus, extract32(addr, 1, 7), extract32(addr, 0, 1))) { - bus->intr_status |= I2CD_INTR_TX_NAK; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + } } else { - bus->intr_status |= I2CD_INTR_TX_ACK; + /* START doesn't set TX_ACK in packet mode */ + if (!aspeed_i2c_bus_pkt_mode_en(bus)) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); + } } - bus->cmd &= ~I2CD_M_START_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_START_CMD, 0); /* * The START command is also a TX command, as the slave * address is sent on the bus. Drop the TX flag if nothing * else needs to be sent in this sequence. */ - if (bus->cmd & I2CD_TX_BUFF_ENABLE) { - if (I2CD_POOL_TX_COUNT(bus->pool_ctrl) == 1) { - bus->cmd &= ~I2CD_M_TX_CMD; + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) + == 1) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); } else { /* * Increase the start index in the TX pool buffer to @@ -477,141 +499,342 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) */ pool_start++; } - } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { - if (bus->dma_len == 0) { - bus->cmd &= ~I2CD_M_TX_CMD; + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + if (bus->regs[reg_dma_len] == 0) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); } } else { - bus->cmd &= ~I2CD_M_TX_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); } /* No slave found */ if (!i2c_bus_busy(bus->bus)) { + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } return; } aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if (bus->cmd & I2CD_M_TX_CMD) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { aspeed_i2c_set_state(bus, I2CD_MTXD); if (aspeed_i2c_bus_send(bus, pool_start)) { - bus->intr_status |= (I2CD_INTR_TX_NAK); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); i2c_end_transfer(bus->bus); } else { - bus->intr_status |= I2CD_INTR_TX_ACK; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); } - bus->cmd &= ~I2CD_M_TX_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if ((bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) && - !(bus->intr_status & I2CD_INTR_RX_DONE)) { + if ((SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) && + !SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE)) { aspeed_i2c_handle_rx_cmd(bus); } - if (bus->cmd & I2CD_M_STOP_CMD) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD)) { if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); - bus->intr_status |= I2CD_INTR_ABNORMAL; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, ABNORMAL, 1); + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + } } else { aspeed_i2c_set_state(bus, I2CD_MSTOP); i2c_end_transfer(bus->bus); - bus->intr_status |= I2CD_INTR_NORMAL_STOP; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1); } - bus->cmd &= ~I2CD_M_STOP_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_STOP_CMD, 0); aspeed_i2c_set_state(bus, I2CD_IDLE); } + + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } } -static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) +static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, + uint64_t value, unsigned size) { - AspeedI2CBus *bus = opaque; AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); bool handle_rx; + bool w1t; trace_aspeed_i2c_bus_write(bus->id, offset, size, value); switch (offset) { - case I2CD_FUN_CTRL_REG: - if (value & I2CD_SLAVE_EN) { - qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", - __func__); - break; - } - bus->ctrl = value & 0x0071C3FF; + case A_I2CC_FUN_CTRL: + bus->regs[R_I2CC_FUN_CTRL] = value; + break; + case A_I2CC_AC_TIMING: + bus->regs[R_I2CC_AC_TIMING] = value & 0x1ffff0ff; break; - case I2CD_AC_TIMING_REG1: - bus->timing[0] = value & 0xFFFFF0F; + case A_I2CC_MS_TXRX_BYTE_BUF: + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_BUF, + value); break; - case I2CD_AC_TIMING_REG2: - bus->timing[1] = value & 0x7; + case A_I2CC_POOL_CTRL: + bus->regs[R_I2CC_POOL_CTRL] &= ~0xffffff; + bus->regs[R_I2CC_POOL_CTRL] |= (value & 0xffffff); break; - case I2CD_INTR_CTRL_REG: - bus->intr_ctrl = value & 0x7FFF; + case A_I2CM_INTR_CTRL: + bus->regs[R_I2CM_INTR_CTRL] = value & 0x0007f07f; break; - case I2CD_INTR_STS_REG: - handle_rx = (bus->intr_status & I2CD_INTR_RX_DONE) && - (value & I2CD_INTR_RX_DONE); - bus->intr_status &= ~(value & 0x7FFF); - if (!bus->intr_status) { + case A_I2CM_INTR_STS: + handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_INTR_STS, RX_DONE) + && SHARED_FIELD_EX32(value, RX_DONE); + + /* In packet mode, clearing PKT_CMD_DONE clears other interrupts. */ + if (aspeed_i2c_bus_pkt_mode_en(bus) && + FIELD_EX32(value, I2CM_INTR_STS, PKT_CMD_DONE)) { + bus->regs[R_I2CM_INTR_STS] &= 0xf0001000; + if (!bus->regs[R_I2CM_INTR_STS]) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(aic->bus_get_irq(bus)); + } + aspeed_i2c_bus_raise_slave_interrupt(bus); + break; + } + bus->regs[R_I2CM_INTR_STS] &= ~(value & 0xf007f07f); + if (!bus->regs[R_I2CM_INTR_STS]) { bus->controller->intr_status &= ~(1 << bus->id); qemu_irq_lower(aic->bus_get_irq(bus)); } - if (handle_rx && (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) { + if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, + M_RX_CMD) || + SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, + M_S_RX_CMD_LAST))) { aspeed_i2c_handle_rx_cmd(bus); aspeed_i2c_bus_raise_interrupt(bus); } break; - case I2CD_DEV_ADDR_REG: - qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + case A_I2CM_CMD: + if (!aspeed_i2c_bus_is_enabled(bus)) { + break; + } + + if (!aspeed_i2c_bus_is_master(bus)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Master mode is not enabled\n", + __func__); + break; + } + + if (!aic->has_dma && + (SHARED_FIELD_EX32(value, RX_DMA_EN) || + SHARED_FIELD_EX32(value, TX_DMA_EN))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + + if (bus->regs[R_I2CM_INTR_STS] & 0xffff0000) { + qemu_log_mask(LOG_UNIMP, "%s: Packet mode is not implemented\n", + __func__); + break; + } + + value &= 0xff0ffbfb; + if (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, W1_CTRL)) { + bus->regs[R_I2CM_CMD] |= value; + } else { + bus->regs[R_I2CM_CMD] = value; + } + + aspeed_i2c_bus_handle_cmd(bus, value); + aspeed_i2c_bus_raise_interrupt(bus); + break; + case A_I2CM_DMA_TX_ADDR: + bus->regs[R_I2CM_DMA_TX_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, + ADDR); + bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, ADDR); + bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, + TX_BUF_LEN) + 1; + break; + case A_I2CM_DMA_RX_ADDR: + bus->regs[R_I2CM_DMA_RX_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, + ADDR); + bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, ADDR); + bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, + RX_BUF_LEN) + 1; + break; + case A_I2CM_DMA_LEN: + w1t = FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN_W1T) || + FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN_W1T); + /* If none of the w1t bits are set, just write to the reg as normal. */ + if (!w1t) { + bus->regs[R_I2CM_DMA_LEN] = value; + break; + } + if (FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN_W1T)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN, + FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN)); + } + if (FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN_W1T)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN, + FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN)); + } + break; + case A_I2CM_DMA_LEN_STS: + /* Writes clear to 0 */ + bus->regs[R_I2CM_DMA_LEN_STS] = 0; + break; + case A_I2CC_DMA_ADDR: + case A_I2CC_DMA_LEN: + /* RO */ + break; + case A_I2CS_DEV_ADDR: + bus->regs[R_I2CS_DEV_ADDR] = value; + break; + case A_I2CS_DMA_RX_ADDR: + bus->regs[R_I2CS_DMA_RX_ADDR] = value; + break; + case A_I2CS_DMA_LEN: + assert(FIELD_EX32(value, I2CS_DMA_LEN, TX_BUF_LEN) == 0); + if (FIELD_EX32(value, I2CS_DMA_LEN, RX_BUF_LEN_W1T)) { + ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN, RX_BUF_LEN, + FIELD_EX32(value, I2CS_DMA_LEN, RX_BUF_LEN)); + } else { + bus->regs[R_I2CS_DMA_LEN] = value; + } + break; + case A_I2CS_CMD: + if (FIELD_EX32(value, I2CS_CMD, W1_CTRL)) { + bus->regs[R_I2CS_CMD] |= value; + } else { + bus->regs[R_I2CS_CMD] = value; + } + i2c_slave_set_address(bus->slave, bus->regs[R_I2CS_DEV_ADDR]); + break; + case A_I2CS_INTR_CTRL: + bus->regs[R_I2CS_INTR_CTRL] = value; + break; + + case A_I2CS_INTR_STS: + if (ARRAY_FIELD_EX32(bus->regs, I2CS_INTR_CTRL, PKT_CMD_DONE)) { + if (ARRAY_FIELD_EX32(bus->regs, I2CS_INTR_STS, PKT_CMD_DONE) && + FIELD_EX32(value, I2CS_INTR_STS, PKT_CMD_DONE)) { + bus->regs[R_I2CS_INTR_STS] &= 0xfffc0000; + } + } else { + bus->regs[R_I2CS_INTR_STS] &= ~value; + } + if (!bus->regs[R_I2CS_INTR_STS]) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(aic->bus_get_irq(bus)); + } + aspeed_i2c_bus_raise_interrupt(bus); + break; + case A_I2CS_DMA_LEN_STS: + bus->regs[R_I2CS_DMA_LEN_STS] = 0; + break; + case A_I2CS_DMA_TX_ADDR: + qemu_log_mask(LOG_UNIMP, "%s: Slave mode DMA TX is not implemented\n", __func__); break; - case I2CD_POOL_CTRL_REG: - bus->pool_ctrl &= ~0xffffff; - bus->pool_ctrl |= (value & 0xffffff); + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } +} + +static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + bool handle_rx; + + trace_aspeed_i2c_bus_write(bus->id, offset, size, value); + + switch (offset) { + case A_I2CD_FUN_CTRL: + if (SHARED_FIELD_EX32(value, SLAVE_EN)) { + i2c_slave_set_address(bus->slave, bus->regs[R_I2CD_DEV_ADDR]); + } + bus->regs[R_I2CD_FUN_CTRL] = value & 0x0071C3FF; + break; + case A_I2CD_AC_TIMING1: + bus->regs[R_I2CD_AC_TIMING1] = value & 0xFFFFF0F; + break; + case A_I2CD_AC_TIMING2: + bus->regs[R_I2CD_AC_TIMING2] = value & 0x7; + break; + case A_I2CD_INTR_CTRL: + bus->regs[R_I2CD_INTR_CTRL] = value & 0x7FFF; + break; + case A_I2CD_INTR_STS: + handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_INTR_STS, RX_DONE) + && SHARED_FIELD_EX32(value, RX_DONE); + bus->regs[R_I2CD_INTR_STS] &= ~(value & 0x7FFF); + if (!bus->regs[R_I2CD_INTR_STS]) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(aic->bus_get_irq(bus)); + } + if (handle_rx) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, M_RX_CMD) || + SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, + M_S_RX_CMD_LAST)) { + aspeed_i2c_handle_rx_cmd(bus); + aspeed_i2c_bus_raise_interrupt(bus); + } else if (aspeed_i2c_get_state(bus) == I2CD_STXD) { + i2c_ack(bus->bus); + } + } + break; + case A_I2CD_DEV_ADDR: + bus->regs[R_I2CD_DEV_ADDR] = value; + break; + case A_I2CD_POOL_CTRL: + bus->regs[R_I2CD_POOL_CTRL] &= ~0xffffff; + bus->regs[R_I2CD_POOL_CTRL] |= (value & 0xffffff); break; - case I2CD_BYTE_BUF_REG: - bus->buf = (value & I2CD_BYTE_BUF_TX_MASK) << I2CD_BYTE_BUF_TX_SHIFT; + case A_I2CD_BYTE_BUF: + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_BYTE_BUF, TX_BUF, value); break; - case I2CD_CMD_REG: + case A_I2CD_CMD: if (!aspeed_i2c_bus_is_enabled(bus)) { break; } if (!aspeed_i2c_bus_is_master(bus)) { - qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: Master mode is not enabled\n", __func__); break; } if (!aic->has_dma && - value & (I2CD_RX_DMA_ENABLE | I2CD_TX_DMA_ENABLE)) { + (SHARED_FIELD_EX32(value, RX_DMA_EN) || + SHARED_FIELD_EX32(value, TX_DMA_EN))) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); break; } + bus->regs[R_I2CD_CMD] &= ~0xFFFF; + bus->regs[R_I2CD_CMD] |= value & 0xFFFF; + aspeed_i2c_bus_handle_cmd(bus, value); aspeed_i2c_bus_raise_interrupt(bus); break; - case I2CD_DMA_ADDR: + case A_I2CD_DMA_ADDR: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); break; } - bus->dma_addr = value & 0x3ffffffc; + bus->regs[R_I2CD_DMA_ADDR] = value & 0x3ffffffc; break; - case I2CD_DMA_LEN: + case A_I2CD_DMA_LEN: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); break; } - bus->dma_len = value & 0xfff; - if (!bus->dma_len) { + bus->regs[R_I2CD_DMA_LEN] = value & 0xfff; + if (!bus->regs[R_I2CD_DMA_LEN]) { qemu_log_mask(LOG_UNIMP, "%s: invalid DMA length\n", __func__); } break; @@ -622,16 +845,34 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, } } +static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CBus *bus = opaque; + if (aspeed_i2c_is_new_mode(bus->controller)) { + aspeed_i2c_bus_new_write(bus, offset, value, size); + } else { + aspeed_i2c_bus_old_write(bus, offset, value, size); + } +} + static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, unsigned size) { AspeedI2CState *s = opaque; switch (offset) { - case I2C_CTRL_STATUS: + case A_I2C_CTRL_STATUS: return s->intr_status; - case I2C_CTRL_GLOBAL: + case A_I2C_CTRL_GLOBAL: return s->ctrl_global; + case A_I2C_CTRL_NEW_CLK_DIVIDER: + if (aspeed_i2c_is_new_mode(s)) { + return s->new_clk_divider; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -647,10 +888,18 @@ static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, AspeedI2CState *s = opaque; switch (offset) { - case I2C_CTRL_GLOBAL: + case A_I2C_CTRL_GLOBAL: s->ctrl_global = value; break; - case I2C_CTRL_STATUS: + case A_I2C_CTRL_NEW_CLK_DIVIDER: + if (aspeed_i2c_is_new_mode(s)) { + s->new_clk_divider = value; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx + "\n", __func__, offset); + } + break; + case A_I2C_CTRL_STATUS: default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -707,19 +956,10 @@ static const MemoryRegionOps aspeed_i2c_pool_ops = { static const VMStateDescription aspeed_i2c_bus_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 3, - .minimum_version_id = 3, + .version_id = 5, + .minimum_version_id = 5, .fields = (VMStateField[]) { - VMSTATE_UINT8(id, AspeedI2CBus), - VMSTATE_UINT32(ctrl, AspeedI2CBus), - VMSTATE_UINT32_ARRAY(timing, AspeedI2CBus, 2), - VMSTATE_UINT32(intr_ctrl, AspeedI2CBus), - VMSTATE_UINT32(intr_status, AspeedI2CBus), - VMSTATE_UINT32(cmd, AspeedI2CBus), - VMSTATE_UINT32(buf, AspeedI2CBus), - VMSTATE_UINT32(pool_ctrl, AspeedI2CBus), - VMSTATE_UINT32(dma_addr, AspeedI2CBus), - VMSTATE_UINT32(dma_len, AspeedI2CBus), + VMSTATE_UINT32_ARRAY(regs, AspeedI2CBus, ASPEED_I2C_NEW_NUM_REG), VMSTATE_END_OF_LIST() } }; @@ -852,16 +1092,132 @@ static const TypeInfo aspeed_i2c_info = { .abstract = true, }; +static int aspeed_i2c_bus_new_slave_event(AspeedI2CBus *bus, + enum i2c_event event) +{ + switch (event) { + case I2C_START_SEND_ASYNC: + if (!SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CS_CMD, RX_DMA_EN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Slave mode RX DMA is not enabled\n", __func__); + return -1; + } + ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN, 0); + bus->regs[R_I2CC_DMA_ADDR] = + ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_RX_ADDR, ADDR); + bus->regs[R_I2CC_DMA_LEN] = + ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_LEN, RX_BUF_LEN) + 1; + i2c_ack(bus->bus); + break; + case I2C_FINISH: + ARRAY_FIELD_DP32(bus->regs, I2CS_INTR_STS, PKT_CMD_DONE, 1); + ARRAY_FIELD_DP32(bus->regs, I2CS_INTR_STS, SLAVE_ADDR_RX_MATCH, 1); + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CS_INTR_STS, NORMAL_STOP, 1); + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CS_INTR_STS, RX_DONE, 1); + aspeed_i2c_bus_raise_slave_interrupt(bus); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: i2c event %d unimplemented\n", + __func__, event); + return -1; + } + + return 0; +} + +static int aspeed_i2c_bus_slave_event(I2CSlave *slave, enum i2c_event event) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(slave)); + AspeedI2CBus *bus = ASPEED_I2C_BUS(qbus->parent); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t value; + + if (aspeed_i2c_is_new_mode(bus->controller)) { + return aspeed_i2c_bus_new_slave_event(bus, event); + } + + switch (event) { + case I2C_START_SEND_ASYNC: + value = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_byte_buf, TX_BUF); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, value << 1); + + ARRAY_FIELD_DP32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 1); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); + + aspeed_i2c_set_state(bus, I2CD_STXD); + + break; + + case I2C_FINISH: + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1); + + aspeed_i2c_set_state(bus, I2CD_IDLE); + + break; + + default: + return -1; + } + + aspeed_i2c_bus_raise_interrupt(bus); + + return 0; +} + +static void aspeed_i2c_bus_new_slave_send_async(AspeedI2CBus *bus, uint8_t data) +{ + assert(address_space_write(&bus->controller->dram_as, + bus->regs[R_I2CC_DMA_ADDR], + MEMTXATTRS_UNSPECIFIED, &data, 1) == MEMTX_OK); + + bus->regs[R_I2CC_DMA_ADDR]++; + bus->regs[R_I2CC_DMA_LEN]--; + ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN, + ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN) + 1); + i2c_ack(bus->bus); +} + +static void aspeed_i2c_bus_slave_send_async(I2CSlave *slave, uint8_t data) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(slave)); + AspeedI2CBus *bus = ASPEED_I2C_BUS(qbus->parent); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + + if (aspeed_i2c_is_new_mode(bus->controller)) { + return aspeed_i2c_bus_new_slave_send_async(bus, data); + } + + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); + + aspeed_i2c_bus_raise_interrupt(bus); +} + +static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + + dc->desc = "Aspeed I2C Bus Slave"; + + sc->event = aspeed_i2c_bus_slave_event; + sc->send_async = aspeed_i2c_bus_slave_send_async; +} + +static const TypeInfo aspeed_i2c_bus_slave_info = { + .name = TYPE_ASPEED_I2C_BUS_SLAVE, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(AspeedI2CBusSlave), + .class_init = aspeed_i2c_bus_slave_class_init, +}; + static void aspeed_i2c_bus_reset(DeviceState *dev) { AspeedI2CBus *s = ASPEED_I2C_BUS(dev); - s->intr_ctrl = 0; - s->intr_status = 0; - s->cmd = 0; - s->buf = 0; - s->dma_addr = 0; - s->dma_len = 0; + memset(s->regs, 0, sizeof(s->regs)); i2c_end_transfer(s->bus); } @@ -881,6 +1237,8 @@ static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); s->bus = i2c_init_bus(dev, name); + s->slave = i2c_slave_create_simple(s->bus, TYPE_ASPEED_I2C_BUS_SLAVE, + 0xff); memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops, s, name, aic->reg_size); @@ -919,9 +1277,10 @@ static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) static uint8_t *aspeed_2400_i2c_bus_pool_base(AspeedI2CBus *bus) { uint8_t *pool_page = - &bus->controller->pool[I2CD_POOL_PAGE_SEL(bus->ctrl) * 0x100]; + &bus->controller->pool[ARRAY_FIELD_EX32(bus->regs, I2CD_FUN_CTRL, + POOL_PAGE_SEL) * 0x100]; - return &pool_page[I2CD_POOL_OFFSET(bus->pool_ctrl)]; + return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; } static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) @@ -1013,13 +1372,38 @@ static const TypeInfo aspeed_2600_i2c_info = { .class_init = aspeed_2600_i2c_class_init, }; +static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); + + dc->desc = "ASPEED 1030 I2C Controller"; + + aic->num_busses = 14; + aic->reg_size = 0x80; + aic->gap = -1; /* no gap */ + aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; + aic->pool_size = 0x200; + aic->pool_base = 0xC00; + aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; + aic->has_dma = true; +} + +static const TypeInfo aspeed_1030_i2c_info = { + .name = TYPE_ASPEED_1030_I2C, + .parent = TYPE_ASPEED_I2C, + .class_init = aspeed_1030_i2c_class_init, +}; + static void aspeed_i2c_register_types(void) { type_register_static(&aspeed_i2c_bus_info); + type_register_static(&aspeed_i2c_bus_slave_info); type_register_static(&aspeed_i2c_info); type_register_static(&aspeed_2400_i2c_info); type_register_static(&aspeed_2500_i2c_info); type_register_static(&aspeed_2600_i2c_info); + type_register_static(&aspeed_1030_i2c_info); } type_init(aspeed_i2c_register_types) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index d0cb2d32fa4..d4ba8146bff 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -13,6 +13,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/main-loop.h" #include "trace.h" #define I2C_BROADCAST 0x00 @@ -62,6 +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); return bus; } @@ -74,7 +76,7 @@ void i2c_slave_set_address(I2CSlave *dev, uint8_t address) /* Return nonzero if bus is busy. */ int i2c_bus_busy(I2CBus *bus) { - return !QLIST_EMPTY(&bus->current_devs); + return !QLIST_EMPTY(&bus->current_devs) || bus->bh; } bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, @@ -159,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t address, start condition. */ if (sc->event) { - trace_i2c_event("start", s->address); + trace_i2c_event(event == I2C_START_SEND ? "start" : "start_async", + s->address); rv = sc->event(s, event); if (rv && !bus->broadcast) { if (bus_scanned) { @@ -180,6 +183,26 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) : I2C_START_SEND); } +void i2c_bus_master(I2CBus *bus, QEMUBH *bh) +{ + if (i2c_bus_busy(bus)) { + I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); + node->bh = bh; + + QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); + + return; + } + + bus->bh = bh; + qemu_bh_schedule(bus->bh); +} + +void i2c_bus_release(I2CBus *bus) +{ + bus->bh = NULL; +} + int i2c_start_recv(I2CBus *bus, uint8_t address) { return i2c_do_start_transfer(bus, address, I2C_START_RECV); @@ -190,6 +213,11 @@ int i2c_start_send(I2CBus *bus, uint8_t address) return i2c_do_start_transfer(bus, address, I2C_START_SEND); } +int i2c_start_send_async(I2CBus *bus, uint8_t address) +{ + return i2c_do_start_transfer(bus, address, I2C_START_SEND_ASYNC); +} + void i2c_end_transfer(I2CBus *bus) { I2CSlaveClass *sc; @@ -206,6 +234,16 @@ void i2c_end_transfer(I2CBus *bus) g_free(node); } bus->broadcast = false; + + if (!QSIMPLEQ_EMPTY(&bus->pending_masters)) { + I2CPendingMaster *node = QSIMPLEQ_FIRST(&bus->pending_masters); + bus->bh = node->bh; + + QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry); + g_free(node); + + qemu_bh_schedule(bus->bh); + } } int i2c_send(I2CBus *bus, uint8_t data) @@ -229,6 +267,23 @@ int i2c_send(I2CBus *bus, uint8_t data) return ret ? -1 : 0; } +int i2c_send_async(I2CBus *bus, uint8_t data) +{ + I2CNode *node = QLIST_FIRST(&bus->current_devs); + I2CSlave *slave = node->elt; + I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(slave); + + if (!sc->send_async) { + return -1; + } + + trace_i2c_send_async(slave->address, data); + + sc->send_async(slave, data); + + return 0; +} + uint8_t i2c_recv(I2CBus *bus) { uint8_t data = 0xff; @@ -265,6 +320,17 @@ void i2c_nack(I2CBus *bus) } } +void i2c_ack(I2CBus *bus) +{ + if (!bus->bh) { + return; + } + + trace_i2c_ack(); + + qemu_bh_schedule(bus->bh); +} + static int i2c_slave_post_load(void *opaque, int version_id) { I2CSlave *dev = opaque; diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index 62885fa6a15..4071a88cfcd 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -261,6 +261,11 @@ void pmbus_check_limits(PMBusDevice *pmdev) } } +void pmbus_idle(PMBusDevice *pmdev) +{ + pmdev->code = PMBUS_IDLE_STATE; +} + /* assert the status_cml error upon receipt of malformed command */ static void pmbus_cml_error(PMBusDevice *pmdev) { @@ -284,14 +289,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) /* * Reading from all pages will return the value from page 0, - * this is unspecified behaviour in general. + * means that all subsequent commands are to be applied to all output. */ if (pmdev->page == PB_ALL_PAGES) { index = 0; - qemu_log_mask(LOG_GUEST_ERROR, - "%s: tried to read from all pages\n", - __func__); - pmbus_cml_error(pmdev); } else if (pmdev->page > pmdev->num_pages - 1) { qemu_log_mask(LOG_GUEST_ERROR, "%s: page %d is out of range\n", @@ -984,6 +985,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_IDLE_STATE: + pmbus_send8(pmdev, PMBUS_ERR_BYTE); + break; + case PMBUS_CLEAR_FAULTS: /* Send Byte */ case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */ diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c index 5d10e27664d..feb3ec63335 100644 --- a/hw/i2c/smbus_slave.c +++ b/hw/i2c/smbus_slave.c @@ -143,6 +143,10 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) dev->mode = SMBUS_CONFUSED; break; } + break; + + default: + return -1; } return 0; diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index 7d8907c1eed..af181d43ee6 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -4,12 +4,14 @@ i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)" i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x" +i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%02x" i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" +i2c_ack(void) "" # aspeed_i2c.c aspeed_i2c_bus_cmd(uint32_t cmd, const char *cmd_flags, uint32_t count, uint32_t intr_status) "handling cmd=0x%x %s count=%d intr=0x%x" -aspeed_i2c_bus_raise_interrupt(uint32_t intr_status, const char *str1, const char *str2, const char *str3, const char *str4, const char *str5) "handled intr=0x%x %s%s%s%s%s" +aspeed_i2c_bus_raise_interrupt(uint32_t intr_status, const char *s) "handled intr=0x%x %s" aspeed_i2c_bus_read(uint32_t busid, uint64_t offset, unsigned size, uint64_t value) "bus[%d]: To 0x%" PRIx64 " of size %u: 0x%" PRIx64 aspeed_i2c_bus_write(uint32_t busid, uint64_t offset, unsigned size, uint64_t value) "bus[%d]: To 0x%" PRIx64 " of size %u: 0x%" PRIx64 aspeed_i2c_bus_send(const char *mode, int i, int count, uint8_t byte) "%s send %d/%d 0x%02x" diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 754f1d0593e..dc929727dcb 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -631,6 +631,14 @@ static void microvm_machine_initfn(Object *obj) qemu_register_powerdown_notifier(&mms->powerdown_req); } +GlobalProperty microvm_properties[] = { + /* + * pcie host bridge (gpex) on microvm has no io address window, + * so reserving io space is not going to work. Turn it off. + */ + { "pcie-root-port", "io-reserve", "0" }, +}; + static void microvm_class_init(ObjectClass *oc, void *data) { X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); @@ -707,6 +715,9 @@ static void microvm_class_init(ObjectClass *oc, void *data) "Set off to disable adding virtio-mmio devices to the kernel cmdline"); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + + compat_props_add(mc->compat_props, microvm_properties, + G_N_ELEMENTS(microvm_properties)); } static const TypeInfo microvm_machine_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0fc2361ffed..a234989ac36 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -82,7 +82,6 @@ static void pc_init1(MachineState *machine, MemoryRegion *system_io = get_system_io(); PCIBus *pci_bus; ISABus *isa_bus; - PCII440FXState *i440fx_state; int piix3_devfn = -1; qemu_irq smi_irq; GSIState *gsi_state; @@ -203,7 +202,6 @@ static void pc_init1(MachineState *machine, pci_bus = i440fx_init(host_type, pci_type, - &i440fx_state, system_memory, system_io, machine->ram_size, x86ms->below_4g_mem_size, x86ms->above_4g_mem_size, @@ -217,7 +215,6 @@ static void pc_init1(MachineState *machine, isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); } else { pci_bus = NULL; - i440fx_state = NULL; isa_bus = isa_bus_new(NULL, get_system_memory(), system_io, &error_abort); pcms->hpet_enabled = false; diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 0731f704106..e4293d6d666 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -15,7 +15,6 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" #include "hw/i386/pc.h" -#include "hw/southbridge/piix.h" #include "hw/irq.h" #include "hw/hw.h" #include "hw/i386/apic-msidef.h" @@ -149,21 +148,9 @@ void xen_piix3_set_irq(void *opaque, int irq_num, int level) irq_num & 3, level); } -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) +int xen_set_pci_link_route(uint8_t link, uint8_t irq) { - int i; - - /* Scan for updates to PCI link routes (0x60-0x63). */ - for (i = 0; i < len; i++) { - uint8_t v = (val >> (8 * i)) & 0xff; - if (v & 0x80) { - v = 0; - } - v &= 0xf; - if (((address + i) >= PIIX_PIRQCA) && ((address + i) <= PIIX_PIRQCD)) { - xen_set_pci_link_route(xen_domid, address + i - PIIX_PIRQCA, v); - } - } + return xendevicemodel_set_pci_link_route(xen_dmod, xen_domid, link, irq); } int xen_is_pirq_msi(uint32_t msi_data) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index b626199e3de..0a9aa6f0091 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -98,11 +98,11 @@ cd_read_sector_sync(IDEState *s) switch (s->cd_sector_size) { case 2048: ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - s->io_buffer, ATAPI_SECTOR_SIZE); + ATAPI_SECTOR_SIZE, s->io_buffer, 0); break; case 2352: ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - s->io_buffer + 16, ATAPI_SECTOR_SIZE); + ATAPI_SECTOR_SIZE, s->io_buffer + 16, 0); if (ret >= 0) { cd_data_to_raw(s->io_buffer, s->lba); } @@ -318,7 +318,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) } } -/* start a CD-CDROM read command */ +/* start a CD-ROM read command */ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, int sector_size) { @@ -417,7 +417,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) ide_set_inactive(s, false); } -/* start a CD-CDROM read command with DMA */ +/* start a CD-ROM read command with DMA */ /* XXX: test if DMA is available */ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, int sector_size) diff --git a/hw/ide/core.c b/hw/ide/core.c index c2caa542855..7cbc0a54a7d 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2548,7 +2548,6 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, s->smart_selftest_count = 0; if (kind == IDE_CD) { blk_set_dev_ops(blk, &ide_cd_block_ops, s); - blk_set_guest_block_size(blk, 2048); } else { if (!blk_is_inserted(s->blk)) { error_setg(errp, "Device needs media, but drive is empty"); diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c index 94f18be4cd5..ea7c07a2ba4 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/qdev-properties.h" +#include "hw/sysbus.h" #include "hw/input/ps2.h" #include "hw/input/lasips2.h" #include "exec/hwaddr.h" @@ -31,37 +32,31 @@ #include "exec/address-spaces.h" #include "migration/vmstate.h" #include "hw/irq.h" +#include "qapi/error.h" -struct LASIPS2State; -typedef struct LASIPS2Port { - struct LASIPS2State *parent; - MemoryRegion reg; - void *dev; - uint8_t id; - uint8_t control; - uint8_t buf; - bool loopback_rbne; - bool irq; -} LASIPS2Port; - -typedef struct LASIPS2State { - LASIPS2Port kbd; - LASIPS2Port mouse; - qemu_irq irq; -} LASIPS2State; +static const VMStateDescription vmstate_lasips2_port = { + .name = "lasips2-port", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(control, LASIPS2Port), + VMSTATE_UINT8(buf, LASIPS2Port), + VMSTATE_BOOL(loopback_rbne, LASIPS2Port), + VMSTATE_END_OF_LIST() + } +}; static const VMStateDescription vmstate_lasips2 = { .name = "lasips2", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT8(kbd.control, LASIPS2State), - VMSTATE_UINT8(kbd.id, LASIPS2State), - VMSTATE_BOOL(kbd.irq, LASIPS2State), - VMSTATE_UINT8(mouse.control, LASIPS2State), - VMSTATE_UINT8(mouse.id, LASIPS2State), - VMSTATE_BOOL(mouse.irq, LASIPS2State), + VMSTATE_UINT8(int_status, LASIPS2State), + VMSTATE_STRUCT(kbd_port.parent_obj, LASIPS2State, 1, + vmstate_lasips2_port, LASIPS2Port), + VMSTATE_STRUCT(mouse_port.parent_obj, LASIPS2State, 1, + vmstate_lasips2_port, LASIPS2Port), VMSTATE_END_OF_LIST() } }; @@ -135,36 +130,50 @@ static const char *lasips2_write_reg_name(uint64_t addr) static void lasips2_update_irq(LASIPS2State *s) { - trace_lasips2_intr(s->kbd.irq | s->mouse.irq); - qemu_set_irq(s->irq, s->kbd.irq | s->mouse.irq); + int level = s->int_status ? 1 : 0; + + trace_lasips2_intr(level); + qemu_set_irq(s->irq, level); +} + +static void lasips2_set_irq(void *opaque, int n, int level) +{ + LASIPS2State *s = LASIPS2(opaque); + + if (level) { + s->int_status |= BIT(n); + } else { + s->int_status &= ~BIT(n); + } + + lasips2_update_irq(s); } static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - LASIPS2Port *port = opaque; + LASIPS2Port *lp = LASIPS2_PORT(opaque); - trace_lasips2_reg_write(size, port->id, addr, + trace_lasips2_reg_write(size, lp->id, addr, lasips2_write_reg_name(addr), val); switch (addr & 0xc) { case REG_PS2_CONTROL: - port->control = val; + lp->control = val; break; case REG_PS2_XMTDATA: - if (port->control & LASIPS2_CONTROL_LOOPBACK) { - port->buf = val; - port->irq = true; - port->loopback_rbne = true; - lasips2_update_irq(port->parent); + if (lp->control & LASIPS2_CONTROL_LOOPBACK) { + lp->buf = val; + lp->loopback_rbne = true; + qemu_set_irq(lp->irq, 1); break; } - if (port->id) { - ps2_write_mouse(port->dev, val); + if (lp->id) { + ps2_write_mouse(PS2_MOUSE_DEVICE(lp->ps2dev), val); } else { - ps2_write_keyboard(port->dev, val); + ps2_write_keyboard(PS2_KBD_DEVICE(lp->ps2dev), val); } break; @@ -180,55 +189,53 @@ static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val, static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size) { - LASIPS2Port *port = opaque; + LASIPS2Port *lp = LASIPS2_PORT(opaque); uint64_t ret = 0; switch (addr & 0xc) { case REG_PS2_ID: - ret = port->id; + ret = lp->id; break; case REG_PS2_RCVDATA: - if (port->control & LASIPS2_CONTROL_LOOPBACK) { - port->irq = false; - port->loopback_rbne = false; - lasips2_update_irq(port->parent); - ret = port->buf; + if (lp->control & LASIPS2_CONTROL_LOOPBACK) { + lp->loopback_rbne = false; + qemu_set_irq(lp->irq, 0); + ret = lp->buf; break; } - ret = ps2_read_data(port->dev); + ret = ps2_read_data(lp->ps2dev); break; case REG_PS2_CONTROL: - ret = port->control; + ret = lp->control; break; case REG_PS2_STATUS: - ret = LASIPS2_STATUS_DATSHD | LASIPS2_STATUS_CLKSHD; - if (port->control & LASIPS2_CONTROL_DIAG) { - if (!(port->control & LASIPS2_CONTROL_DATDIR)) { + if (lp->control & LASIPS2_CONTROL_DIAG) { + if (!(lp->control & LASIPS2_CONTROL_DATDIR)) { ret &= ~LASIPS2_STATUS_DATSHD; } - if (!(port->control & LASIPS2_CONTROL_CLKDIR)) { + if (!(lp->control & LASIPS2_CONTROL_CLKDIR)) { ret &= ~LASIPS2_STATUS_CLKSHD; } } - if (port->control & LASIPS2_CONTROL_LOOPBACK) { - if (port->loopback_rbne) { + if (lp->control & LASIPS2_CONTROL_LOOPBACK) { + if (lp->loopback_rbne) { ret |= LASIPS2_STATUS_RBNE; } } else { - if (!ps2_queue_empty(port->dev)) { + if (!ps2_queue_empty(lp->ps2dev)) { ret |= LASIPS2_STATUS_RBNE; } } - if (port->parent->kbd.irq || port->parent->mouse.irq) { + if (lp->lasips2->int_status) { ret |= LASIPS2_STATUS_CMPINTR; } break; @@ -238,9 +245,9 @@ static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size) __func__, addr); break; } - trace_lasips2_reg_read(size, port->id, addr, - lasips2_read_reg_name(addr), ret); + trace_lasips2_reg_read(size, lp->id, addr, + lasips2_read_reg_name(addr), ret); return ret; } @@ -251,38 +258,208 @@ static const MemoryRegionOps lasips2_reg_ops = { .min_access_size = 1, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void lasips2_realize(DeviceState *dev, Error **errp) +{ + LASIPS2State *s = LASIPS2(dev); + LASIPS2Port *lp; + + lp = LASIPS2_PORT(&s->kbd_port); + if (!(qdev_realize(DEVICE(lp), NULL, errp))) { + return; + } + + qdev_connect_gpio_out(DEVICE(lp), 0, + qdev_get_gpio_in_named(dev, "lasips2-port-input-irq", + lp->id)); + + lp = LASIPS2_PORT(&s->mouse_port); + if (!(qdev_realize(DEVICE(lp), NULL, errp))) { + return; + } + + qdev_connect_gpio_out(DEVICE(lp), 0, + qdev_get_gpio_in_named(dev, "lasips2-port-input-irq", + lp->id)); +} + +static void lasips2_init(Object *obj) +{ + LASIPS2State *s = LASIPS2(obj); + LASIPS2Port *lp; + + object_initialize_child(obj, "lasips2-kbd-port", &s->kbd_port, + TYPE_LASIPS2_KBD_PORT); + object_initialize_child(obj, "lasips2-mouse-port", &s->mouse_port, + TYPE_LASIPS2_MOUSE_PORT); + + lp = LASIPS2_PORT(&s->kbd_port); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &lp->reg); + lp = LASIPS2_PORT(&s->mouse_port); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &lp->reg); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + qdev_init_gpio_in_named(DEVICE(obj), lasips2_set_irq, + "lasips2-port-input-irq", 2); +} + +static void lasips2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = lasips2_realize; + dc->vmsd = &vmstate_lasips2; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo lasips2_info = { + .name = TYPE_LASIPS2, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = lasips2_init, + .instance_size = sizeof(LASIPS2State), + .class_init = lasips2_class_init, +}; + +static void lasips2_port_set_irq(void *opaque, int n, int level) +{ + LASIPS2Port *s = LASIPS2_PORT(opaque); + + qemu_set_irq(s->irq, level); +} + +static void lasips2_port_realize(DeviceState *dev, Error **errp) +{ + LASIPS2Port *s = LASIPS2_PORT(dev); + + qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-input-irq", 0)); +} + +static void lasips2_port_init(Object *obj) +{ + LASIPS2Port *s = LASIPS2_PORT(obj); + + qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); + qdev_init_gpio_in_named(DEVICE(obj), lasips2_port_set_irq, + "ps2-input-irq", 1); +} + +static void lasips2_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = lasips2_port_realize; +} + +static const TypeInfo lasips2_port_info = { + .name = TYPE_LASIPS2_PORT, + .parent = TYPE_DEVICE, + .instance_init = lasips2_port_init, + .instance_size = sizeof(LASIPS2Port), + .class_init = lasips2_port_class_init, + .class_size = sizeof(LASIPS2PortDeviceClass), + .abstract = true, }; -static void ps2dev_update_irq(void *opaque, int level) +static void lasips2_kbd_port_realize(DeviceState *dev, Error **errp) { - LASIPS2Port *port = opaque; - port->irq = level; - lasips2_update_irq(port->parent); + LASIPS2KbdPort *s = LASIPS2_KBD_PORT(dev); + LASIPS2Port *lp = LASIPS2_PORT(dev); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_GET_CLASS(lp); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) { + return; + } + + lp->ps2dev = PS2_DEVICE(&s->kbd); + lpdc->parent_realize(dev, errp); } -void lasips2_init(MemoryRegion *address_space, - hwaddr base, qemu_irq irq) +static void lasips2_kbd_port_init(Object *obj) { - LASIPS2State *s; + LASIPS2KbdPort *s = LASIPS2_KBD_PORT(obj); + LASIPS2Port *lp = LASIPS2_PORT(obj); - s = g_new0(LASIPS2State, 1); + memory_region_init_io(&lp->reg, obj, &lasips2_reg_ops, lp, "lasips2-kbd", + 0x100); - s->irq = irq; - s->mouse.id = 1; - s->kbd.parent = s; - s->mouse.parent = s; + object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE); - vmstate_register(NULL, base, &vmstate_lasips2, s); + lp->id = 0; + lp->lasips2 = container_of(s, LASIPS2State, kbd_port); +} - s->kbd.dev = ps2_kbd_init(ps2dev_update_irq, &s->kbd); - s->mouse.dev = ps2_mouse_init(ps2dev_update_irq, &s->mouse); +static void lasips2_kbd_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); - memory_region_init_io(&s->kbd.reg, NULL, &lasips2_reg_ops, &s->kbd, - "lasips2-kbd", 0x100); - memory_region_add_subregion(address_space, base, &s->kbd.reg); + device_class_set_parent_realize(dc, lasips2_kbd_port_realize, + &lpdc->parent_realize); +} + +static const TypeInfo lasips2_kbd_port_info = { + .name = TYPE_LASIPS2_KBD_PORT, + .parent = TYPE_LASIPS2_PORT, + .instance_size = sizeof(LASIPS2KbdPort), + .instance_init = lasips2_kbd_port_init, + .class_init = lasips2_kbd_port_class_init, +}; - memory_region_init_io(&s->mouse.reg, NULL, &lasips2_reg_ops, &s->mouse, - "lasips2-mouse", 0x100); - memory_region_add_subregion(address_space, base + 0x100, &s->mouse.reg); +static void lasips2_mouse_port_realize(DeviceState *dev, Error **errp) +{ + LASIPS2MousePort *s = LASIPS2_MOUSE_PORT(dev); + LASIPS2Port *lp = LASIPS2_PORT(dev); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_GET_CLASS(lp); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) { + return; + } + + lp->ps2dev = PS2_DEVICE(&s->mouse); + lpdc->parent_realize(dev, errp); +} + +static void lasips2_mouse_port_init(Object *obj) +{ + LASIPS2MousePort *s = LASIPS2_MOUSE_PORT(obj); + LASIPS2Port *lp = LASIPS2_PORT(obj); + + memory_region_init_io(&lp->reg, obj, &lasips2_reg_ops, lp, "lasips2-mouse", + 0x100); + + object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); + + lp->id = 1; + lp->lasips2 = container_of(s, LASIPS2State, mouse_port); } + +static void lasips2_mouse_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); + + device_class_set_parent_realize(dc, lasips2_mouse_port_realize, + &lpdc->parent_realize); +} + +static const TypeInfo lasips2_mouse_port_info = { + .name = TYPE_LASIPS2_MOUSE_PORT, + .parent = TYPE_LASIPS2_PORT, + .instance_size = sizeof(LASIPS2MousePort), + .instance_init = lasips2_mouse_port_init, + .class_init = lasips2_mouse_port_class_init, +}; + +static void lasips2_register_types(void) +{ + type_register_static(&lasips2_info); + type_register_static(&lasips2_port_info); + type_register_static(&lasips2_kbd_port_info); + type_register_static(&lasips2_mouse_port_info); +} + +type_init(lasips2_register_types) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 45c40fe3f34..b92b63bedca 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -39,49 +39,86 @@ #include "trace.h" -/* Keyboard Controller Commands */ -#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ -#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ -#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ -#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ -#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ -#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ -#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ -#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ -#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ -#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ -#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */ -#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */ -#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */ -#define KBD_CCMD_WRITE_OBUF 0xD2 -#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if - initiated by the auxiliary device */ -#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ -#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ -#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ -#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */ -#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */ -#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */ +/* Keyboard Controller Commands */ + +/* Read mode bits */ +#define KBD_CCMD_READ_MODE 0x20 +/* Write mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 +/* Get controller version */ +#define KBD_CCMD_GET_VERSION 0xA1 +/* Disable mouse interface */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 +/* Enable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 +/* Mouse interface test */ +#define KBD_CCMD_TEST_MOUSE 0xA9 +/* Controller self test */ +#define KBD_CCMD_SELF_TEST 0xAA +/* Keyboard interface test */ +#define KBD_CCMD_KBD_TEST 0xAB +/* Keyboard interface disable */ +#define KBD_CCMD_KBD_DISABLE 0xAD +/* Keyboard interface enable */ +#define KBD_CCMD_KBD_ENABLE 0xAE +/* read input port */ +#define KBD_CCMD_READ_INPORT 0xC0 +/* read output port */ +#define KBD_CCMD_READ_OUTPORT 0xD0 +/* write output port */ +#define KBD_CCMD_WRITE_OUTPORT 0xD1 +#define KBD_CCMD_WRITE_OBUF 0xD2 +/* Write to output buffer as if initiated by the auxiliary device */ +#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 +/* Write the following byte to the mouse */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 +/* HP vectra only ? */ +#define KBD_CCMD_DISABLE_A20 0xDD +/* HP vectra only ? */ +#define KBD_CCMD_ENABLE_A20 0xDF +/* Pulse bits 3-0 of the output port P2. */ +#define KBD_CCMD_PULSE_BITS_3_0 0xF0 +/* Pulse bit 0 of the output port P2 = CPU reset. */ +#define KBD_CCMD_RESET 0xFE +/* Pulse no bits of the output port P2. */ +#define KBD_CCMD_NO_OP 0xFF /* Status Register Bits */ -#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ -#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ -#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ -#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ -#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ -#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ -#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ -#define KBD_STAT_PERR 0x80 /* Parity error */ + +/* Keyboard output buffer full */ +#define KBD_STAT_OBF 0x01 +/* Keyboard input buffer full */ +#define KBD_STAT_IBF 0x02 +/* Self test successful */ +#define KBD_STAT_SELFTEST 0x04 +/* Last write was a command write (0=data) */ +#define KBD_STAT_CMD 0x08 +/* Zero if keyboard locked */ +#define KBD_STAT_UNLOCKED 0x10 +/* Mouse output buffer full */ +#define KBD_STAT_MOUSE_OBF 0x20 +/* General receive/xmit timeout */ +#define KBD_STAT_GTO 0x40 +/* Parity error */ +#define KBD_STAT_PERR 0x80 /* Controller Mode Register Bits */ -#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ -#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ -#define KBD_MODE_SYS 0x04 /* The system flag (?) */ -#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ -#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ -#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ -#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ -#define KBD_MODE_RFU 0x80 + +/* Keyboard data generate IRQ1 */ +#define KBD_MODE_KBD_INT 0x01 +/* Mouse data generate IRQ12 */ +#define KBD_MODE_MOUSE_INT 0x02 +/* The system flag (?) */ +#define KBD_MODE_SYS 0x04 +/* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_NO_KEYLOCK 0x08 +/* Disable keyboard interface */ +#define KBD_MODE_DISABLE_KBD 0x10 +/* Disable mouse interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 +/* Scan code conversion to PC format */ +#define KBD_MODE_KCC 0x40 +#define KBD_MODE_RFU 0x80 /* Output Port Bits */ #define KBD_OUT_RESET 0x01 /* 1=normal mode, 0=reset */ @@ -89,7 +126,8 @@ #define KBD_OUT_OBF 0x10 /* Keyboard output buffer full */ #define KBD_OUT_MOUSE_OBF 0x20 /* Mouse output buffer full */ -/* OSes typically write 0xdd/0xdf to turn the A20 line off and on. +/* + * OSes typically write 0xdd/0xdf to turn the A20 line off and on. * We make the default value of the outport include these four bits, * so that the subsection is rarely necessary. */ @@ -108,33 +146,11 @@ #define KBD_OBSRC_MOUSE 0x02 #define KBD_OBSRC_CTRL 0x04 -typedef struct KBDState { - uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ - uint8_t status; - uint8_t mode; - uint8_t outport; - uint32_t migration_flags; - uint32_t obsrc; - bool outport_present; - bool extended_state; - bool extended_state_loaded; - /* Bitmask of devices with data available. */ - uint8_t pending; - uint8_t obdata; - uint8_t cbdata; - uint8_t pending_tmp; - void *kbd; - void *mouse; - QEMUTimer *throttle_timer; - - qemu_irq irq_kbd; - qemu_irq irq_mouse; - qemu_irq a20_out; - hwaddr mask; -} KBDState; - -/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be - incorrect, but it avoids having to simulate exact delays */ + +/* + * XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be + * incorrect, but it avoids having to simulate exact delays + */ static void kbd_update_irq_lines(KBDState *s) { int irq_kbd_level, irq_mouse_level; @@ -154,8 +170,8 @@ static void kbd_update_irq_lines(KBDState *s) } } } - qemu_set_irq(s->irq_kbd, irq_kbd_level); - qemu_set_irq(s->irq_mouse, irq_mouse_level); + qemu_set_irq(s->irqs[I8042_KBD_IRQ], irq_kbd_level); + qemu_set_irq(s->irqs[I8042_MOUSE_IRQ], irq_mouse_level); } static void kbd_deassert_irq(KBDState *s) @@ -270,7 +286,7 @@ static void kbd_queue(KBDState *s, int b, int aux) s->pending |= aux ? KBD_PENDING_CTRL_AUX : KBD_PENDING_CTRL_KBD; kbd_safe_update_irq(s); } else { - ps2_queue(aux ? s->mouse : s->kbd, b); + ps2_queue(aux ? PS2_DEVICE(&s->ps2mouse) : PS2_DEVICE(&s->ps2kbd), b); } } @@ -302,21 +318,23 @@ static void kbd_write_command(void *opaque, hwaddr addr, trace_pckbd_kbd_write_command(val); - /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed + /* + * Bits 3-0 of the output port P2 of the keyboard controller may be pulsed * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE * command specify the output port bits to be pulsed. * 0: Bit should be pulsed. 1: Bit should not be modified. * The only useful version of this command is pulsing bit 0, * which does a CPU reset. */ - if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { - if(!(val & 1)) + if ((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { + if (!(val & 1)) { val = KBD_CCMD_RESET; - else + } else { val = KBD_CCMD_NO_OP; + } } - switch(val) { + switch (val) { case KBD_CCMD_READ_MODE: kbd_queue(s, s->mode, 0); break; @@ -390,9 +408,9 @@ static uint64_t kbd_read_data(void *opaque, hwaddr addr, timer_mod(s->throttle_timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 1000); } - s->obdata = ps2_read_data(s->kbd); + s->obdata = ps2_read_data(PS2_DEVICE(&s->ps2kbd)); } else if (s->obsrc & KBD_OBSRC_MOUSE) { - s->obdata = ps2_read_data(s->mouse); + s->obdata = ps2_read_data(PS2_DEVICE(&s->ps2mouse)); } else if (s->obsrc & KBD_OBSRC_CTRL) { s->obdata = kbd_dequeue(s); } @@ -409,16 +427,17 @@ static void kbd_write_data(void *opaque, hwaddr addr, trace_pckbd_kbd_write_data(val); - switch(s->write_cmd) { + switch (s->write_cmd) { case 0: - ps2_write_keyboard(s->kbd, val); + ps2_write_keyboard(&s->ps2kbd, val); /* sending data to the keyboard reenables PS/2 communication */ s->mode &= ~KBD_MODE_DISABLE_KBD; kbd_safe_update_irq(s); break; case KBD_CCMD_WRITE_MODE: s->mode = val; - ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0); + ps2_keyboard_set_translation(&s->ps2kbd, + (s->mode & KBD_MODE_KCC) != 0); /* * a write to the mode byte interrupt enable flags directly updates * the irq lines @@ -440,7 +459,7 @@ static void kbd_write_data(void *opaque, hwaddr addr, outport_write(s, val); break; case KBD_CCMD_WRITE_MOUSE: - ps2_write_mouse(s->mouse, val); + ps2_write_mouse(&s->ps2mouse, val); /* sending data to the mouse reenables PS/2 communication */ s->mode &= ~KBD_MODE_DISABLE_MOUSE; kbd_safe_update_irq(s); @@ -607,7 +626,7 @@ static const VMStateDescription vmstate_kbd = { VMSTATE_UINT8(pending_tmp, KBDState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * []) { &vmstate_kbd_outport, &vmstate_kbd_extended_state, NULL @@ -619,10 +638,11 @@ static uint64_t kbd_mm_readfn(void *opaque, hwaddr addr, unsigned size) { KBDState *s = opaque; - if (addr & s->mask) + if (addr & s->mask) { return kbd_read_status(s, 0, 1) & 0xff; - else + } else { return kbd_read_data(s, 0, 1) & 0xff; + } } static void kbd_mm_writefn(void *opaque, hwaddr addr, @@ -630,10 +650,11 @@ static void kbd_mm_writefn(void *opaque, hwaddr addr, { KBDState *s = opaque; - if (addr & s->mask) + if (addr & s->mask) { kbd_write_command(s, 0, value & 0xff, 1); - else + } else { kbd_write_data(s, 0, value & 0xff, 1); + } } @@ -645,42 +666,115 @@ static const MemoryRegionOps i8042_mmio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, - MemoryRegion *region, ram_addr_t size, - hwaddr mask) +static void i8042_mmio_set_kbd_irq(void *opaque, int n, int level) { - KBDState *s = g_new0(KBDState, 1); + MMIOKBDState *s = I8042_MMIO(opaque); + KBDState *ks = &s->kbd; - s->irq_kbd = kbd_irq; - s->irq_mouse = mouse_irq; - s->mask = mask; + kbd_update_kbd_irq(ks, level); +} - s->extended_state = true; +static void i8042_mmio_set_mouse_irq(void *opaque, int n, int level) +{ + MMIOKBDState *s = I8042_MMIO(opaque); + KBDState *ks = &s->kbd; - vmstate_register(NULL, 0, &vmstate_kbd, s); + kbd_update_aux_irq(ks, level); +} - memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size); +static void i8042_mmio_reset(DeviceState *dev) +{ + MMIOKBDState *s = I8042_MMIO(dev); + KBDState *ks = &s->kbd; - s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); - s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); - qemu_register_reset(kbd_reset, s); + kbd_reset(ks); } -struct ISAKBDState { - ISADevice parent_obj; +static void i8042_mmio_realize(DeviceState *dev, Error **errp) +{ + MMIOKBDState *s = I8042_MMIO(dev); + KBDState *ks = &s->kbd; + + memory_region_init_io(&s->region, OBJECT(dev), &i8042_mmio_ops, ks, + "i8042", s->size); + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->region); + + if (!sysbus_realize(SYS_BUS_DEVICE(&ks->ps2kbd), errp)) { + return; + } + + if (!sysbus_realize(SYS_BUS_DEVICE(&ks->ps2mouse), errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&ks->ps2kbd), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-kbd-input-irq", + 0)); + + qdev_connect_gpio_out(DEVICE(&ks->ps2mouse), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-mouse-input-irq", + 0)); +} + +static void i8042_mmio_init(Object *obj) +{ + MMIOKBDState *s = I8042_MMIO(obj); + KBDState *ks = &s->kbd; + + ks->extended_state = true; + + object_initialize_child(obj, "ps2kbd", &ks->ps2kbd, TYPE_PS2_KBD_DEVICE); + object_initialize_child(obj, "ps2mouse", &ks->ps2mouse, + TYPE_PS2_MOUSE_DEVICE); + + qdev_init_gpio_out(DEVICE(obj), ks->irqs, 2); + qdev_init_gpio_in_named(DEVICE(obj), i8042_mmio_set_kbd_irq, + "ps2-kbd-input-irq", 1); + qdev_init_gpio_in_named(DEVICE(obj), i8042_mmio_set_mouse_irq, + "ps2-mouse-input-irq", 1); +} + +static Property i8042_mmio_properties[] = { + DEFINE_PROP_UINT64("mask", MMIOKBDState, kbd.mask, UINT64_MAX), + DEFINE_PROP_UINT32("size", MMIOKBDState, size, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_kbd_mmio = { + .name = "pckbd-mmio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(kbd, MMIOKBDState, 0, vmstate_kbd, KBDState), + VMSTATE_END_OF_LIST() + } +}; - KBDState kbd; - bool kbd_throttle; - MemoryRegion io[2]; - uint8_t kbd_irq; - uint8_t mouse_irq; +static void i8042_mmio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = i8042_mmio_realize; + dc->reset = i8042_mmio_reset; + dc->vmsd = &vmstate_kbd_mmio; + device_class_set_props(dc, i8042_mmio_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo i8042_mmio_info = { + .name = TYPE_I8042_MMIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = i8042_mmio_init, + .instance_size = sizeof(MMIOKBDState), + .class_init = i8042_mmio_class_init }; void i8042_isa_mouse_fake_event(ISAKBDState *isa) { KBDState *s = &isa->kbd; - ps2_mouse_fake_event(s->mouse); + ps2_mouse_fake_event(&s->ps2mouse); } void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out) @@ -718,6 +812,31 @@ static const MemoryRegionOps i8042_cmd_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void i8042_set_kbd_irq(void *opaque, int n, int level) +{ + ISAKBDState *s = I8042(opaque); + KBDState *ks = &s->kbd; + + kbd_update_kbd_irq(ks, level); +} + +static void i8042_set_mouse_irq(void *opaque, int n, int level) +{ + ISAKBDState *s = I8042(opaque); + KBDState *ks = &s->kbd; + + kbd_update_aux_irq(ks, level); +} + + +static void i8042_reset(DeviceState *dev) +{ + ISAKBDState *s = I8042(dev); + KBDState *ks = &s->kbd; + + kbd_reset(ks); +} + static void i8042_initfn(Object *obj) { ISAKBDState *isa_s = I8042(obj); @@ -728,7 +847,17 @@ static void i8042_initfn(Object *obj) memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s, "i8042-cmd", 1); + object_initialize_child(obj, "ps2kbd", &s->ps2kbd, TYPE_PS2_KBD_DEVICE); + object_initialize_child(obj, "ps2mouse", &s->ps2mouse, + TYPE_PS2_MOUSE_DEVICE); + qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, I8042_A20_LINE, 1); + + qdev_init_gpio_out(DEVICE(obj), s->irqs, 2); + qdev_init_gpio_in_named(DEVICE(obj), i8042_set_kbd_irq, + "ps2-kbd-input-irq", 1); + qdev_init_gpio_in_named(DEVICE(obj), i8042_set_mouse_irq, + "ps2-mouse-input-irq", 1); } static void i8042_realizefn(DeviceState *dev, Error **errp) @@ -749,14 +878,28 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) return; } - s->irq_kbd = isa_get_irq(isadev, isa_s->kbd_irq); - s->irq_mouse = isa_get_irq(isadev, isa_s->mouse_irq); + isa_connect_gpio_out(isadev, I8042_KBD_IRQ, isa_s->kbd_irq); + isa_connect_gpio_out(isadev, I8042_MOUSE_IRQ, isa_s->mouse_irq); isa_register_ioport(isadev, isa_s->io + 0, 0x60); isa_register_ioport(isadev, isa_s->io + 1, 0x64); - s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); - s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ps2kbd), errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->ps2kbd), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-kbd-input-irq", + 0)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ps2mouse), errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->ps2mouse), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-mouse-input-irq", + 0)); + if (isa_s->kbd_throttle && !isa_s->kbd.extended_state) { warn_report(TYPE_I8042 ": can't enable kbd-throttle without" " extended-state, disabling kbd-throttle"); @@ -764,7 +907,6 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) s->throttle_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, kbd_throttle_timeout, s); } - qemu_register_reset(kbd_reset, s); } static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope) @@ -810,6 +952,7 @@ static void i8042_class_initfn(ObjectClass *klass, void *data) AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); device_class_set_props(dc, i8042_properties); + dc->reset = i8042_reset; dc->realize = i8042_realizefn; dc->vmsd = &vmstate_kbd_isa; adevc->build_dev_aml = i8042_build_aml; @@ -831,6 +974,7 @@ static const TypeInfo i8042_info = { static void i8042_register_types(void) { type_register_static(&i8042_info); + type_register_static(&i8042_mmio_info); } type_init(i8042_register_types) diff --git a/hw/input/pl050.c b/hw/input/pl050.c index d279b6c1488..ec5e19285e3 100644 --- a/hw/input/pl050.c +++ b/hw/input/pl050.c @@ -7,30 +7,24 @@ * This code is licensed under the GPL. */ +/* + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion defining the PL050 registers + * + Named GPIO input "ps2-input-irq": set to 1 if the downstream PS2 device + * has asserted its irq + * + sysbus IRQ 0: PL050 output irq + */ + #include "qemu/osdep.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/input/ps2.h" +#include "hw/input/pl050.h" #include "hw/irq.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" -#define TYPE_PL050 "pl050" -OBJECT_DECLARE_SIMPLE_TYPE(PL050State, PL050) - -struct PL050State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - void *dev; - uint32_t cr; - uint32_t clk; - uint32_t last; - int pending; - qemu_irq irq; - bool is_mouse; -}; static const VMStateDescription vmstate_pl050 = { .name = "pl050", @@ -53,26 +47,34 @@ static const VMStateDescription vmstate_pl050 = { #define PL050_KMIC (1 << 1) #define PL050_KMID (1 << 0) -static const unsigned char pl050_id[] = -{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; +static const unsigned char pl050_id[] = { + 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; -static void pl050_update(void *opaque, int level) +static void pl050_update_irq(PL050State *s) +{ + int level = (s->pending && (s->cr & 0x10) != 0) + || (s->cr & 0x08) != 0; + + qemu_set_irq(s->irq, level); +} + +static void pl050_set_irq(void *opaque, int n, int level) { PL050State *s = (PL050State *)opaque; - int raise; s->pending = level; - raise = (s->pending && (s->cr & 0x10) != 0) - || (s->cr & 0x08) != 0; - qemu_set_irq(s->irq, raise); + pl050_update_irq(s); } static uint64_t pl050_read(void *opaque, hwaddr offset, unsigned size) { PL050State *s = (PL050State *)opaque; - if (offset >= 0xfe0 && offset < 0x1000) + + if (offset >= 0xfe0 && offset < 0x1000) { return pl050_id[(offset - 0xfe0) >> 2]; + } switch (offset >> 2) { case 0: /* KMICR */ @@ -88,16 +90,19 @@ static uint64_t pl050_read(void *opaque, hwaddr offset, val = (val ^ (val >> 1)) & 1; stat = PL050_TXEMPTY; - if (val) + if (val) { stat |= PL050_RXPARITY; - if (s->pending) + } + if (s->pending) { stat |= PL050_RXFULL; + } return stat; } case 2: /* KMIDATA */ - if (s->pending) - s->last = ps2_read_data(s->dev); + if (s->pending) { + s->last = ps2_read_data(s->ps2dev); + } return s->last; case 3: /* KMICLKDIV */ return s->clk; @@ -114,19 +119,20 @@ static void pl050_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PL050State *s = (PL050State *)opaque; + switch (offset >> 2) { case 0: /* KMICR */ s->cr = value; - pl050_update(s, s->pending); + pl050_update_irq(s); /* ??? Need to implement the enable/disable bit. */ break; case 2: /* KMIDATA */ /* ??? This should toggle the TX interrupt line. */ /* ??? This means kbd/mouse can block each other. */ if (s->is_mouse) { - ps2_write_mouse(s->dev, value); + ps2_write_mouse(PS2_MOUSE_DEVICE(s->ps2dev), value); } else { - ps2_write_keyboard(s->dev, value); + ps2_write_keyboard(PS2_KBD_DEVICE(s->ps2dev), value); } break; case 3: /* KMICLKDIV */ @@ -146,44 +152,103 @@ static const MemoryRegionOps pl050_ops = { static void pl050_realize(DeviceState *dev, Error **errp) { PL050State *s = PL050(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &pl050_ops, s, "pl050", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - if (s->is_mouse) { - s->dev = ps2_mouse_init(pl050_update, s); - } else { - s->dev = ps2_kbd_init(pl050_update, s); + qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-input-irq", 0)); +} + +static void pl050_kbd_realize(DeviceState *dev, Error **errp) +{ + PL050DeviceClass *pdc = PL050_GET_CLASS(dev); + PL050KbdState *s = PL050_KBD_DEVICE(dev); + PL050State *ps = PL050(dev); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) { + return; } + + ps->ps2dev = PS2_DEVICE(&s->kbd); + pdc->parent_realize(dev, errp); } -static void pl050_keyboard_init(Object *obj) +static void pl050_kbd_init(Object *obj) { - PL050State *s = PL050(obj); + PL050KbdState *s = PL050_KBD_DEVICE(obj); + PL050State *ps = PL050(obj); - s->is_mouse = false; + ps->is_mouse = false; + object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE); +} + +static void pl050_mouse_realize(DeviceState *dev, Error **errp) +{ + PL050DeviceClass *pdc = PL050_GET_CLASS(dev); + PL050MouseState *s = PL050_MOUSE_DEVICE(dev); + PL050State *ps = PL050(dev); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) { + return; + } + + ps->ps2dev = PS2_DEVICE(&s->mouse); + pdc->parent_realize(dev, errp); } static void pl050_mouse_init(Object *obj) { - PL050State *s = PL050(obj); + PL050MouseState *s = PL050_MOUSE_DEVICE(obj); + PL050State *ps = PL050(obj); - s->is_mouse = true; + ps->is_mouse = true; + object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); +} + +static void pl050_kbd_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PL050DeviceClass *pdc = PL050_CLASS(oc); + + device_class_set_parent_realize(dc, pl050_kbd_realize, + &pdc->parent_realize); } static const TypeInfo pl050_kbd_info = { - .name = "pl050_keyboard", + .name = TYPE_PL050_KBD_DEVICE, .parent = TYPE_PL050, - .instance_init = pl050_keyboard_init, + .instance_init = pl050_kbd_init, + .instance_size = sizeof(PL050KbdState), + .class_init = pl050_kbd_class_init, }; +static void pl050_mouse_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PL050DeviceClass *pdc = PL050_CLASS(oc); + + device_class_set_parent_realize(dc, pl050_mouse_realize, + &pdc->parent_realize); +} + static const TypeInfo pl050_mouse_info = { - .name = "pl050_mouse", + .name = TYPE_PL050_MOUSE_DEVICE, .parent = TYPE_PL050, .instance_init = pl050_mouse_init, + .instance_size = sizeof(PL050MouseState), + .class_init = pl050_mouse_class_init, }; +static void pl050_init(Object *obj) +{ + PL050State *s = PL050(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &pl050_ops, s, "pl050", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + + qdev_init_gpio_in_named(DEVICE(obj), pl050_set_irq, "ps2-input-irq", 1); +} + static void pl050_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -195,7 +260,10 @@ static void pl050_class_init(ObjectClass *oc, void *data) static const TypeInfo pl050_type_info = { .name = TYPE_PL050, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = pl050_init, .instance_size = sizeof(PL050State), + .class_init = pl050_class_init, + .class_size = sizeof(PL050DeviceClass), .abstract = true, .class_init = pl050_class_init, }; diff --git a/hw/input/ps2.c b/hw/input/ps2.c index c16df1de7ae..05cf7111e31 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -24,61 +24,59 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/irq.h" +#include "hw/sysbus.h" #include "hw/input/ps2.h" #include "migration/vmstate.h" #include "ui/console.h" #include "ui/input.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" +#include "qapi/error.h" #include "trace.h" /* Keyboard Commands */ -#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ -#define KBD_CMD_ECHO 0xEE -#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */ -#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ -#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ -#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ -#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ -#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ -#define KBD_CMD_RESET 0xFF /* Reset */ +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_ECHO 0xEE +#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */ +#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ +#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ #define KBD_CMD_SET_MAKE_BREAK 0xFC /* Set Make and Break mode */ #define KBD_CMD_SET_TYPEMATIC 0xFA /* Set Typematic Make and Break mode */ /* Keyboard Replies */ -#define KBD_REPLY_POR 0xAA /* Power on reset */ -#define KBD_REPLY_ID 0xAB /* Keyboard ID */ -#define KBD_REPLY_ACK 0xFA /* Command ACK */ -#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ID 0xAB /* Keyboard ID */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ /* Mouse Commands */ -#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ -#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ -#define AUX_SET_RES 0xE8 /* Set resolution */ -#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ -#define AUX_SET_STREAM 0xEA /* Set stream mode */ -#define AUX_POLL 0xEB /* Poll */ -#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ -#define AUX_SET_WRAP 0xEE /* Set wrap mode */ -#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ -#define AUX_GET_TYPE 0xF2 /* Get type */ -#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ -#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ -#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ -#define AUX_SET_DEFAULT 0xF6 -#define AUX_RESET 0xFF /* Reset aux device */ -#define AUX_ACK 0xFA /* Command byte ACK. */ +#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define AUX_SET_RES 0xE8 /* Set resolution */ +#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ +#define AUX_SET_STREAM 0xEA /* Set stream mode */ +#define AUX_POLL 0xEB /* Poll */ +#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ +#define AUX_SET_WRAP 0xEE /* Set wrap mode */ +#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ +#define AUX_GET_TYPE 0xF2 /* Get type */ +#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ +#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ +#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ +#define AUX_SET_DEFAULT 0xF6 +#define AUX_RESET 0xFF /* Reset aux device */ +#define AUX_ACK 0xFA /* Command byte ACK. */ #define MOUSE_STATUS_REMOTE 0x40 #define MOUSE_STATUS_ENABLED 0x20 #define MOUSE_STATUS_SCALE21 0x10 -/* - * PS/2 buffer size. Keep 256 bytes for compatibility with - * older QEMU versions. - */ -#define PS2_BUFFER_SIZE 256 #define PS2_QUEUE_SIZE 16 /* Queue size required by PS/2 protocol */ #define PS2_QUEUE_HEADROOM 8 /* Queue size for keyboard command replies */ @@ -90,43 +88,6 @@ #define MOD_SHIFT_R (1 << 4) #define MOD_ALT_R (1 << 5) -typedef struct { - uint8_t data[PS2_BUFFER_SIZE]; - int rptr, wptr, cwptr, count; -} PS2Queue; - -struct PS2State { - PS2Queue queue; - int32_t write_cmd; - void (*update_irq)(void *, int); - void *update_arg; -}; - -typedef struct { - PS2State common; - int scan_enabled; - int translate; - int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ - int ledstate; - bool need_high_bit; - unsigned int modifiers; /* bitmask of MOD_* constants above */ -} PS2KbdState; - -typedef struct { - PS2State common; - uint8_t mouse_status; - uint8_t mouse_resolution; - uint8_t mouse_sample_rate; - uint8_t mouse_wrap; - uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ - uint8_t mouse_detect_state; - int mouse_dx; /* current values, needed for 'poll' mode */ - int mouse_dy; - int mouse_dz; - int mouse_dw; - uint8_t mouse_buttons; -} PS2MouseState; - static uint8_t translate_table[256] = { 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, @@ -212,9 +173,14 @@ void ps2_queue_noirq(PS2State *s, int b) q->count++; } -void ps2_raise_irq(PS2State *s) +static void ps2_raise_irq(PS2State *s) +{ + qemu_set_irq(s->irq, 1); +} + +static void ps2_lower_irq(PS2State *s) { - s->update_irq(s->update_arg, 1); + qemu_set_irq(s->irq, 0); } void ps2_queue(PS2State *s, int b) @@ -324,6 +290,7 @@ static void ps2_cqueue_reset(PS2State *s) static void ps2_put_keycode(void *opaque, int keycode) { PS2KbdState *s = opaque; + PS2State *ps = PS2_DEVICE(s); trace_ps2_put_keycode(opaque, keycode); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); @@ -332,13 +299,13 @@ static void ps2_put_keycode(void *opaque, int keycode) if (keycode == 0xf0) { s->need_high_bit = true; } else if (s->need_high_bit) { - ps2_queue(&s->common, translate_table[keycode] | 0x80); + ps2_queue(ps, translate_table[keycode] | 0x80); s->need_high_bit = false; } else { - ps2_queue(&s->common, translate_table[keycode]); + ps2_queue(ps, translate_table[keycode]); } } else { - ps2_queue(&s->common, keycode); + ps2_queue(ps, keycode); } } @@ -436,8 +403,9 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } } else { - if (qcode < qemu_input_map_qcode_to_atset1_len) + if (qcode < qemu_input_map_qcode_to_atset1_len) { keycode = qemu_input_map_qcode_to_atset1[qcode]; + } if (keycode) { if (keycode & 0xff00) { ps2_put_keycode(s, keycode >> 8); @@ -530,8 +498,9 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } } else { - if (qcode < qemu_input_map_qcode_to_atset2_len) + if (qcode < qemu_input_map_qcode_to_atset2_len) { keycode = qemu_input_map_qcode_to_atset2[qcode]; + } if (keycode) { if (keycode & 0xff00) { ps2_put_keycode(s, keycode >> 8); @@ -546,8 +515,9 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } } else if (s->scancode_set == 3) { - if (qcode < qemu_input_map_qcode_to_atset3_len) + if (qcode < qemu_input_map_qcode_to_atset3_len) { keycode = qemu_input_map_qcode_to_atset3[qcode]; + } if (keycode) { /* FIXME: break code should be configured on a key by key basis */ if (!key->down) { @@ -569,8 +539,10 @@ uint32_t ps2_read_data(PS2State *s) trace_ps2_read_data(s); q = &s->queue; if (q->count == 0) { - /* NOTE: if no data left, we return the last keyboard one - (needed for EMM386) */ + /* + * NOTE: if no data left, we return the last keyboard one + * (needed for EMM386) + */ /* XXX: need a timer to do things correctly */ index = q->rptr - 1; if (index < 0) { @@ -588,10 +560,10 @@ uint32_t ps2_read_data(PS2State *s) q->cwptr = -1; } /* reading deasserts IRQ */ - s->update_irq(s->update_arg, 0); + ps2_lower_irq(s); /* reassert IRQs if data left */ if (q->count) { - s->update_irq(s->update_arg, 1); + ps2_raise_irq(s); } } return val; @@ -606,119 +578,123 @@ static void ps2_set_ledstate(PS2KbdState *s, int ledstate) static void ps2_reset_keyboard(PS2KbdState *s) { + PS2State *ps2 = PS2_DEVICE(s); + trace_ps2_reset_keyboard(s); s->scan_enabled = 1; s->scancode_set = 2; - ps2_reset_queue(&s->common); + ps2_reset_queue(ps2); ps2_set_ledstate(s, 0); } -void ps2_write_keyboard(void *opaque, int val) +void ps2_write_keyboard(PS2KbdState *s, int val) { - PS2KbdState *s = (PS2KbdState *)opaque; + PS2State *ps2 = PS2_DEVICE(s); - trace_ps2_write_keyboard(opaque, val); - ps2_cqueue_reset(&s->common); - switch(s->common.write_cmd) { + trace_ps2_write_keyboard(s, val); + ps2_cqueue_reset(ps2); + switch (ps2->write_cmd) { default: case -1: - switch(val) { + switch (val) { case 0x00: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case 0x05: - ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(ps2, KBD_REPLY_RESEND); break; case KBD_CMD_GET_ID: /* We emulate a MF2 AT keyboard here */ - ps2_cqueue_3(&s->common, KBD_REPLY_ACK, KBD_REPLY_ID, - s->translate ? 0x41 : 0x83); + ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID, + s->translate ? 0x41 : 0x83); break; case KBD_CMD_ECHO: - ps2_cqueue_1(&s->common, KBD_CMD_ECHO); + ps2_cqueue_1(ps2, KBD_CMD_ECHO); break; case KBD_CMD_ENABLE: s->scan_enabled = 1; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_SCANCODE: case KBD_CMD_SET_LEDS: case KBD_CMD_SET_RATE: case KBD_CMD_SET_MAKE_BREAK: - s->common.write_cmd = val; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2->write_cmd = val; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_RESET_DISABLE: ps2_reset_keyboard(s); s->scan_enabled = 0; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_RESET_ENABLE: ps2_reset_keyboard(s); s->scan_enabled = 1; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_RESET: ps2_reset_keyboard(s); - ps2_cqueue_2(&s->common, - KBD_REPLY_ACK, - KBD_REPLY_POR); + ps2_cqueue_2(ps2, + KBD_REPLY_ACK, + KBD_REPLY_POR); break; case KBD_CMD_SET_TYPEMATIC: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; default: - ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(ps2, KBD_REPLY_RESEND); break; } break; case KBD_CMD_SET_MAKE_BREAK: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); + ps2->write_cmd = -1; break; case KBD_CMD_SCANCODE: if (val == 0) { - ps2_cqueue_2(&s->common, KBD_REPLY_ACK, s->translate ? + ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ? translate_table[s->scancode_set] : s->scancode_set); } else if (val >= 1 && val <= 3) { s->scancode_set = val; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); } else { - ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(ps2, KBD_REPLY_RESEND); } - s->common.write_cmd = -1; + ps2->write_cmd = -1; break; case KBD_CMD_SET_LEDS: ps2_set_ledstate(s, val); - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); + ps2->write_cmd = -1; break; case KBD_CMD_SET_RATE: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); + ps2->write_cmd = -1; break; } } -/* Set the scancode translation mode. - 0 = raw scancodes. - 1 = translated scancodes (used by qemu internally). */ +/* + * Set the scancode translation mode. + * 0 = raw scancodes. + * 1 = translated scancodes (used by qemu internally). + */ -void ps2_keyboard_set_translation(void *opaque, int mode) +void ps2_keyboard_set_translation(PS2KbdState *s, int mode) { - PS2KbdState *s = (PS2KbdState *)opaque; - trace_ps2_keyboard_set_translation(opaque, mode); + trace_ps2_keyboard_set_translation(s, mode); s->translate = mode; } static int ps2_mouse_send_packet(PS2MouseState *s) { + PS2State *ps2 = PS2_DEVICE(s); /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */ const int needed = s->mouse_type ? 4 : 3; unsigned int b; int dx1, dy1, dz1, dw1; - if (PS2_QUEUE_SIZE - s->common.queue.count < needed) { + if (PS2_QUEUE_SIZE - ps2->queue.count < needed) { return 0; } @@ -727,31 +703,34 @@ static int ps2_mouse_send_packet(PS2MouseState *s) dz1 = s->mouse_dz; dw1 = s->mouse_dw; /* XXX: increase range to 8 bits ? */ - if (dx1 > 127) + if (dx1 > 127) { dx1 = 127; - else if (dx1 < -127) + } else if (dx1 < -127) { dx1 = -127; - if (dy1 > 127) + } + if (dy1 > 127) { dy1 = 127; - else if (dy1 < -127) + } else if (dy1 < -127) { dy1 = -127; + } b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); - ps2_queue_noirq(&s->common, b); - ps2_queue_noirq(&s->common, dx1 & 0xff); - ps2_queue_noirq(&s->common, dy1 & 0xff); + ps2_queue_noirq(ps2, b); + ps2_queue_noirq(ps2, dx1 & 0xff); + ps2_queue_noirq(ps2, dy1 & 0xff); /* extra byte for IMPS/2 or IMEX */ - switch(s->mouse_type) { + switch (s->mouse_type) { default: /* Just ignore the wheels if not supported */ s->mouse_dz = 0; s->mouse_dw = 0; break; case 3: - if (dz1 > 127) + if (dz1 > 127) { dz1 = 127; - else if (dz1 < -127) - dz1 = -127; - ps2_queue_noirq(&s->common, dz1 & 0xff); + } else if (dz1 < -127) { + dz1 = -127; + } + ps2_queue_noirq(ps2, dz1 & 0xff); s->mouse_dz -= dz1; s->mouse_dw = 0; break; @@ -787,11 +766,11 @@ static int ps2_mouse_send_packet(PS2MouseState *s) b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); s->mouse_dz -= dz1; } - ps2_queue_noirq(&s->common, b); + ps2_queue_noirq(ps2, b); break; } - ps2_raise_irq(&s->common); + ps2_raise_irq(ps2); trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); /* update deltas */ @@ -816,8 +795,9 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, InputBtnEvent *btn; /* check if deltas are recorded when disabled */ - if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) + if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) { return; + } switch (evt->type) { case INPUT_EVENT_KIND_REL: @@ -868,8 +848,10 @@ static void ps2_mouse_sync(DeviceState *dev) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); } if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { - /* if not remote, send event. Multiple events are sent if - too big deltas */ + /* + * if not remote, send event. Multiple events are sent if + * too big deltas + */ while (ps2_mouse_send_packet(s)) { if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && s->mouse_dw == 0) { @@ -879,96 +861,95 @@ static void ps2_mouse_sync(DeviceState *dev) } } -void ps2_mouse_fake_event(void *opaque) +void ps2_mouse_fake_event(PS2MouseState *s) { - PS2MouseState *s = opaque; - trace_ps2_mouse_fake_event(opaque); + trace_ps2_mouse_fake_event(s); s->mouse_dx++; - ps2_mouse_sync(opaque); + ps2_mouse_sync(DEVICE(s)); } -void ps2_write_mouse(void *opaque, int val) +void ps2_write_mouse(PS2MouseState *s, int val) { - PS2MouseState *s = (PS2MouseState *)opaque; + PS2State *ps2 = PS2_DEVICE(s); - trace_ps2_write_mouse(opaque, val); - switch(s->common.write_cmd) { + trace_ps2_write_mouse(s, val); + switch (ps2->write_cmd) { default: case -1: /* mouse command */ if (s->mouse_wrap) { if (val == AUX_RESET_WRAP) { s->mouse_wrap = 0; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); return; } else if (val != AUX_RESET) { - ps2_queue(&s->common, val); + ps2_queue(ps2, val); return; } } - switch(val) { + switch (val) { case AUX_SET_SCALE11: s->mouse_status &= ~MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_SCALE21: s->mouse_status |= MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_STREAM: s->mouse_status &= ~MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_WRAP: s->mouse_wrap = 1; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_REMOTE: s->mouse_status |= MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_GET_TYPE: - ps2_queue_2(&s->common, + ps2_queue_2(ps2, AUX_ACK, s->mouse_type); break; case AUX_SET_RES: case AUX_SET_SAMPLE: - s->common.write_cmd = val; - ps2_queue(&s->common, AUX_ACK); + ps2->write_cmd = val; + ps2_queue(ps2, AUX_ACK); break; case AUX_GET_SCALE: - ps2_queue_4(&s->common, + ps2_queue_4(ps2, AUX_ACK, s->mouse_status, s->mouse_resolution, s->mouse_sample_rate); break; case AUX_POLL: - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); ps2_mouse_send_packet(s); break; case AUX_ENABLE_DEV: s->mouse_status |= MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_DISABLE_DEV: s->mouse_status &= ~MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_DEFAULT: s->mouse_sample_rate = 100; s->mouse_resolution = 2; s->mouse_status = 0; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_RESET: s->mouse_sample_rate = 100; s->mouse_resolution = 2; s->mouse_status = 0; s->mouse_type = 0; - ps2_reset_queue(&s->common); - ps2_queue_3(&s->common, + ps2_reset_queue(ps2); + ps2_queue_3(ps2, AUX_ACK, 0xaa, s->mouse_type); @@ -980,47 +961,53 @@ void ps2_write_mouse(void *opaque, int val) case AUX_SET_SAMPLE: s->mouse_sample_rate = val; /* detect IMPS/2 or IMEX */ - switch(s->mouse_detect_state) { + switch (s->mouse_detect_state) { default: case 0: - if (val == 200) + if (val == 200) { s->mouse_detect_state = 1; + } break; case 1: - if (val == 100) + if (val == 100) { s->mouse_detect_state = 2; - else if (val == 200) + } else if (val == 200) { s->mouse_detect_state = 3; - else + } else { s->mouse_detect_state = 0; + } break; case 2: - if (val == 80) + if (val == 80) { s->mouse_type = 3; /* IMPS/2 */ + } s->mouse_detect_state = 0; break; case 3: - if (val == 80) + if (val == 80) { s->mouse_type = 4; /* IMEX */ + } s->mouse_detect_state = 0; break; } - ps2_queue(&s->common, AUX_ACK); - s->common.write_cmd = -1; + ps2_queue(ps2, AUX_ACK); + ps2->write_cmd = -1; break; case AUX_SET_RES: s->mouse_resolution = val; - ps2_queue(&s->common, AUX_ACK); - s->common.write_cmd = -1; + ps2_queue(ps2, AUX_ACK); + ps2->write_cmd = -1; break; } } -static void ps2_common_reset(PS2State *s) +static void ps2_reset(DeviceState *dev) { + PS2State *s = PS2_DEVICE(dev); + s->write_cmd = -1; ps2_reset_queue(s); - s->update_irq(s->update_arg, 0); + ps2_lower_irq(s); } static void ps2_common_post_load(PS2State *s) @@ -1049,24 +1036,28 @@ static void ps2_common_post_load(PS2State *s) q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } -static void ps2_kbd_reset(void *opaque) +static void ps2_kbd_reset(DeviceState *dev) { - PS2KbdState *s = (PS2KbdState *) opaque; + PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev); + PS2KbdState *s = PS2_KBD_DEVICE(dev); + + trace_ps2_kbd_reset(s); + ps2dc->parent_reset(dev); - trace_ps2_kbd_reset(opaque); - ps2_common_reset(&s->common); s->scan_enabled = 1; s->translate = 0; s->scancode_set = 2; s->modifiers = 0; } -static void ps2_mouse_reset(void *opaque) +static void ps2_mouse_reset(DeviceState *dev) { - PS2MouseState *s = (PS2MouseState *) opaque; + PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev); + PS2MouseState *s = PS2_MOUSE_DEVICE(dev); + + trace_ps2_mouse_reset(s); + ps2dc->parent_reset(dev); - trace_ps2_mouse_reset(opaque); - ps2_common_reset(&s->common); s->mouse_status = 0; s->mouse_resolution = 0; s->mouse_sample_rate = 0; @@ -1141,26 +1132,28 @@ static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = { static bool ps2_keyboard_cqueue_needed(void *opaque) { PS2KbdState *s = opaque; + PS2State *ps2 = PS2_DEVICE(s); - return s->common.queue.cwptr != -1; /* the queue is mostly empty */ + return ps2->queue.cwptr != -1; /* the queue is mostly empty */ } static const VMStateDescription vmstate_ps2_keyboard_cqueue = { .name = "ps2kbd/command_reply_queue", .needed = ps2_keyboard_cqueue_needed, .fields = (VMStateField[]) { - VMSTATE_INT32(common.queue.cwptr, PS2KbdState), + VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState), VMSTATE_END_OF_LIST() } }; -static int ps2_kbd_post_load(void* opaque, int version_id) +static int ps2_kbd_post_load(void *opaque, int version_id) { - PS2KbdState *s = (PS2KbdState*)opaque; - PS2State *ps2 = &s->common; + PS2KbdState *s = (PS2KbdState *)opaque; + PS2State *ps2 = PS2_DEVICE(s); - if (version_id == 2) - s->scancode_set=2; + if (version_id == 2) { + s->scancode_set = 2; + } ps2_common_post_load(ps2); @@ -1173,13 +1166,14 @@ static const VMStateDescription vmstate_ps2_keyboard = { .minimum_version_id = 2, .post_load = ps2_kbd_post_load, .fields = (VMStateField[]) { - VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State), + VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common, + PS2State), VMSTATE_INT32(scan_enabled, PS2KbdState), VMSTATE_INT32(translate, PS2KbdState), - VMSTATE_INT32_V(scancode_set, PS2KbdState,3), + VMSTATE_INT32_V(scancode_set, PS2KbdState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * []) { &vmstate_ps2_keyboard_ledstate, &vmstate_ps2_keyboard_need_high_bit, &vmstate_ps2_keyboard_cqueue, @@ -1190,7 +1184,7 @@ static const VMStateDescription vmstate_ps2_keyboard = { static int ps2_mouse_post_load(void *opaque, int version_id) { PS2MouseState *s = (PS2MouseState *)opaque; - PS2State *ps2 = &s->common; + PS2State *ps2 = PS2_DEVICE(s); ps2_common_post_load(ps2); @@ -1203,7 +1197,8 @@ static const VMStateDescription vmstate_ps2_mouse = { .minimum_version_id = 2, .post_load = ps2_mouse_post_load, .fields = (VMStateField[]) { - VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State), + VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common, + PS2State), VMSTATE_UINT8(mouse_status, PS2MouseState), VMSTATE_UINT8(mouse_resolution, PS2MouseState), VMSTATE_UINT8(mouse_sample_rate, PS2MouseState), @@ -1224,19 +1219,9 @@ static QemuInputHandler ps2_keyboard_handler = { .event = ps2_keyboard_event, }; -void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) +static void ps2_kbd_realize(DeviceState *dev, Error **errp) { - PS2KbdState *s = g_new0(PS2KbdState, 1); - - trace_ps2_kbd_init(s); - s->common.update_irq = update_irq; - s->common.update_arg = update_arg; - s->scancode_set = 2; - vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s); - qemu_input_handler_register((DeviceState *)s, - &ps2_keyboard_handler); - qemu_register_reset(ps2_kbd_reset, s); - return s; + qemu_input_handler_register(dev, &ps2_keyboard_handler); } static QemuInputHandler ps2_mouse_handler = { @@ -1246,16 +1231,76 @@ static QemuInputHandler ps2_mouse_handler = { .sync = ps2_mouse_sync, }; -void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) +static void ps2_mouse_realize(DeviceState *dev, Error **errp) +{ + qemu_input_handler_register(dev, &ps2_mouse_handler); +} + +static void ps2_kbd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass); + + dc->realize = ps2_kbd_realize; + device_class_set_parent_reset(dc, ps2_kbd_reset, &ps2dc->parent_reset); + dc->vmsd = &vmstate_ps2_keyboard; +} + +static const TypeInfo ps2_kbd_info = { + .name = TYPE_PS2_KBD_DEVICE, + .parent = TYPE_PS2_DEVICE, + .instance_size = sizeof(PS2KbdState), + .class_init = ps2_kbd_class_init +}; + +static void ps2_mouse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass); + + dc->realize = ps2_mouse_realize; + device_class_set_parent_reset(dc, ps2_mouse_reset, + &ps2dc->parent_reset); + dc->vmsd = &vmstate_ps2_mouse; +} + +static const TypeInfo ps2_mouse_info = { + .name = TYPE_PS2_MOUSE_DEVICE, + .parent = TYPE_PS2_DEVICE, + .instance_size = sizeof(PS2MouseState), + .class_init = ps2_mouse_class_init +}; + +static void ps2_init(Object *obj) +{ + PS2State *s = PS2_DEVICE(obj); + + qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); +} + +static void ps2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = ps2_reset; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo ps2_info = { + .name = TYPE_PS2_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = ps2_init, + .instance_size = sizeof(PS2State), + .class_init = ps2_class_init, + .class_size = sizeof(PS2DeviceClass), + .abstract = true +}; + +static void ps2_register_types(void) { - PS2MouseState *s = g_new0(PS2MouseState, 1); - - trace_ps2_mouse_init(s); - s->common.update_irq = update_irq; - s->common.update_arg = update_arg; - vmstate_register(NULL, 0, &vmstate_ps2_mouse, s); - qemu_input_handler_register((DeviceState *)s, - &ps2_mouse_handler); - qemu_register_reset(ps2_mouse_reset, s); - return s; + type_register_static(&ps2_info); + type_register_static(&ps2_kbd_info); + type_register_static(&ps2_mouse_info); } + +type_init(ps2_register_types) diff --git a/hw/input/trace-events b/hw/input/trace-events index e0bfe7f3ee4..29001a827d9 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -41,8 +41,6 @@ ps2_mouse_fake_event(void *opaque) "%p" ps2_write_mouse(void *opaque, int val) "%p val %d" ps2_kbd_reset(void *opaque) "%p" ps2_mouse_reset(void *opaque) "%p" -ps2_kbd_init(void *s) "%p" -ps2_mouse_init(void *s) "%p" # hid.c hid_kbd_queue_full(void) "queue full" diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 13df002ce4d..1f7763964c3 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2389,8 +2389,15 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr, startvec = 8 * (offset - 0x280) + NVIC_FIRST_IRQ; /* vector # */ for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + /* + * Note that if the input line is still held high and the interrupt + * is not active then rule R_CVJS requires that the Pending state + * remains set; in that case we mustn't let it be cleared. + */ if (value & (1 << i) && - (attrs.secure || s->itns[startvec + i])) { + (attrs.secure || s->itns[startvec + i]) && + !(setval == 0 && s->vectors[startvec + i].level && + !s->vectors[startvec + i].active)) { s->vectors[startvec + i].pending = setval; } } diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 66bee93675e..4f3c58f8721 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -50,35 +50,45 @@ static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) return ret; } -static int get_ipi_data(target_ulong val) +static void send_ipi_data(CPULoongArchState *env, target_ulong val, target_ulong addr) { - int i, mask, data; + int i, mask = 0, data = 0; - data = val >> 32; - mask = (val >> 27) & 0xf; - - for (i = 0; i < 4; i++) { - if ((mask >> i) & 1) { - data &= ~(0xff << (i * 8)); + /* + * bit 27-30 is mask for byte writing, + * if the mask is 0, we need not to do anything. + */ + if ((val >> 27) & 0xf) { + data = address_space_ldl(&env->address_space_iocsr, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ + if (val & (0x1 << (27 + i))) { + mask |= 0xff << (i * 8); + } } } - return data; + + data &= mask; + data |= (val >> 32) & ~mask; + address_space_stl(&env->address_space_iocsr, addr, + data, MEMTXATTRS_UNSPECIFIED, NULL); } static void ipi_send(uint64_t val) { int cpuid, data; CPULoongArchState *env; + CPUState *cs; + LoongArchCPU *cpu; cpuid = (val >> 16) & 0x3ff; /* IPI status vector */ data = 1 << (val & 0x1f); - qemu_mutex_lock_iothread(); - CPUState *cs = qemu_get_cpu(cpuid); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); + cs = qemu_get_cpu(cpuid); + cpu = LOONGARCH_CPU(cs); env = &cpu->env; loongarch_cpu_set_irq(cpu, IRQ_IPI, 1); - qemu_mutex_unlock_iothread(); address_space_stl(&env->address_space_iocsr, 0x1008, data, MEMTXATTRS_UNSPECIFIED, NULL); @@ -86,23 +96,23 @@ static void ipi_send(uint64_t val) static void mail_send(uint64_t val) { - int cpuid, data; + int cpuid; hwaddr addr; CPULoongArchState *env; + CPUState *cs; + LoongArchCPU *cpu; cpuid = (val >> 16) & 0x3ff; addr = 0x1020 + (val & 0x1c); - CPUState *cs = qemu_get_cpu(cpuid); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); + cs = qemu_get_cpu(cpuid); + cpu = LOONGARCH_CPU(cs); env = &cpu->env; - data = get_ipi_data(val); - address_space_stl(&env->address_space_iocsr, addr, - data, MEMTXATTRS_UNSPECIFIED, NULL); + send_ipi_data(env, val, addr); } static void any_send(uint64_t val) { - int cpuid, data; + int cpuid; hwaddr addr; CPULoongArchState *env; @@ -111,9 +121,7 @@ static void any_send(uint64_t val) CPUState *cs = qemu_get_cpu(cpuid); LoongArchCPU *cpu = LOONGARCH_CPU(cs); env = &cpu->env; - data = get_ipi_data(val); - address_space_stl(&env->address_space_iocsr, addr, - data, MEMTXATTRS_UNSPECIFIED, NULL); + send_ipi_data(env, val, addr); } static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, @@ -150,12 +158,6 @@ static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, case IOCSR_IPI_SEND: ipi_send(val); break; - case IOCSR_MAIL_SEND: - mail_send(val); - break; - case IOCSR_ANY_SEND: - any_send(val); - break; default: qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); break; @@ -172,6 +174,32 @@ static const MemoryRegionOps loongarch_ipi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +/* mail send and any send only support writeq */ +static void loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + addr &= 0xfff; + switch (addr) { + case MAIL_SEND_OFFSET: + mail_send(val); + break; + case ANY_SEND_OFFSET: + any_send(val); + break; + default: + break; + } +} + +static const MemoryRegionOps loongarch_ipi64_ops = { + .write = loongarch_ipi_writeq, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void loongarch_ipi_init(Object *obj) { int cpu; @@ -187,8 +215,12 @@ static void loongarch_ipi_init(Object *obj) lams = LOONGARCH_MACHINE(machine); for (cpu = 0; cpu < MAX_IPI_CORE_NUM; cpu++) { memory_region_init_io(&s->ipi_iocsr_mem[cpu], obj, &loongarch_ipi_ops, - &lams->ipi_core[cpu], "loongarch_ipi_iocsr", 0x100); + &lams->ipi_core[cpu], "loongarch_ipi_iocsr", 0x48); sysbus_init_mmio(sbd, &s->ipi_iocsr_mem[cpu]); + + memory_region_init_io(&s->ipi64_iocsr_mem[cpu], obj, &loongarch_ipi64_ops, + &lams->ipi_core[cpu], "loongarch_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem[cpu]); qdev_init_gpio_out(DEVICE(obj), &lams->ipi_core[cpu].irq, 1); } } diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index 74bcdbdb482..b36d6d76e4d 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -23,9 +23,14 @@ static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size) static void loongarch_msi_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque); - int irq_num = val & 0xff; + LoongArchPCHMSI *s = (LoongArchPCHMSI *)opaque; + int irq_num; + /* + * vector number is irq number from upper extioi intc + * need subtract irq base to get msi vector offset + */ + irq_num = (val & 0xff) - s->irq_base; trace_loongarch_msi_set_irq(irq_num); assert(irq_num < PCH_MSI_IRQ_NUM); qemu_set_irq(s->pch_msi_irq[irq_num], 1); @@ -58,11 +63,24 @@ static void loongarch_pch_msi_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), pch_msi_irq_handler, PCH_MSI_IRQ_NUM); } +static Property loongarch_msi_properties[] = { + DEFINE_PROP_UINT32("msi_irq_base", LoongArchPCHMSI, irq_base, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void loongarch_pch_msi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, loongarch_msi_properties); +} + static const TypeInfo loongarch_pch_msi_info = { .name = TYPE_LOONGARCH_PCH_MSI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongArchPCHMSI), .instance_init = loongarch_pch_msi_init, + .class_init = loongarch_pch_msi_class_init, }; static void loongarch_pch_msi_register_types(void) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 3c9814a3b43..3380b09807c 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -15,21 +15,21 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) { - unsigned long val; + uint64_t val; int irq; if (level) { val = mask & s->intirr & ~s->int_mask; if (val) { - irq = find_first_bit(&val, 64); - s->intisr |= 0x1ULL << irq; + irq = ctz64(val); + s->intisr |= MAKE_64BIT_MASK(irq, 1); qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); } } else { val = mask & s->intisr; if (val) { - irq = find_first_bit(&val, 64); - s->intisr &= ~(0x1ULL << irq); + irq = ctz64(val); + s->intisr &= ~MAKE_64BIT_MASK(irq, 1); qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); } } diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 49504e740f3..b0787e8ee78 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -729,7 +729,7 @@ static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled) } /* - * Returns the currrent tccr value, i.e., timer value (in clocks) with + * Returns the current tccr value, i.e., timer value (in clocks) with * appropriate TOG. */ static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 1ce1d7b07d6..c7b75ed12ee 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -66,26 +66,6 @@ static const XiveVstInfo vst_infos[] = { qemu_log_mask(LOG_GUEST_ERROR, "XIVE[%x] - " fmt "\n", \ (xive)->chip->chip_id, ## __VA_ARGS__); -/* - * QEMU version of the GETFIELD/SETFIELD macros - * - * TODO: It might be better to use the existing extract64() and - * deposit64() but this means that all the register definitions will - * change and become incompatible with the ones found in skiboot. - * - * Keep it as it is for now until we find a common ground. - */ -static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) -{ - return (word & mask) >> ctz64(mask); -} - -static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, - uint64_t value) -{ - return (word & ~mask) | ((value << ctz64(mask)) & mask); -} - /* * When PC_TCTXT_CHIPID_OVERRIDE is configured, the PC_TCTXT_CHIPID * field overrides the hardwired chip ID in the Powerbus operations diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index a39e070e82d..f22ce5ca59a 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -75,26 +75,6 @@ static const XiveVstInfo vst_infos[] = { qemu_log_mask(LOG_GUEST_ERROR, "XIVE[%x] - " fmt "\n", \ (xive)->chip->chip_id, ## __VA_ARGS__); -/* - * QEMU version of the GETFIELD/SETFIELD macros - * - * TODO: It might be better to use the existing extract64() and - * deposit64() but this means that all the register definitions will - * change and become incompatible with the ones found in skiboot. - * - * Keep it as it is for now until we find a common ground. - */ -static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) -{ - return (word & mask) >> ctz64(mask); -} - -static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, - uint64_t value) -{ - return (word & ~mask) | ((value << ctz64(mask)) & mask); -} - /* * TODO: Document block id override */ @@ -1574,6 +1554,12 @@ static const MemoryRegionOps pnv_xive2_ic_sync_ops = { * When the TM direct pages of the IC controller are accessed, the * target HW thread is deduced from the page offset. */ +static uint32_t pnv_xive2_ic_tm_get_pir(PnvXive2 *xive, hwaddr offset) +{ + /* On P10, the node ID shift in the PIR register is 8 bits */ + return xive->chip->chip_id << 8 | offset >> xive->ic_shift; +} + static XiveTCTX *pnv_xive2_get_indirect_tctx(PnvXive2 *xive, uint32_t pir) { PnvChip *chip = xive->chip; @@ -1596,10 +1582,12 @@ static uint64_t pnv_xive2_ic_tm_indirect_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); - uint32_t pir = offset >> xive->ic_shift; - XiveTCTX *tctx = pnv_xive2_get_indirect_tctx(xive, pir); + uint32_t pir; + XiveTCTX *tctx; uint64_t val = -1; + pir = pnv_xive2_ic_tm_get_pir(xive, offset); + tctx = pnv_xive2_get_indirect_tctx(xive, pir); if (tctx) { val = xive_tctx_tm_read(NULL, tctx, offset, size); } @@ -1611,9 +1599,11 @@ static void pnv_xive2_ic_tm_indirect_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); - uint32_t pir = offset >> xive->ic_shift; - XiveTCTX *tctx = pnv_xive2_get_indirect_tctx(xive, pir); + uint32_t pir; + XiveTCTX *tctx; + pir = pnv_xive2_ic_tm_get_pir(xive, offset); + tctx = pnv_xive2_get_indirect_tctx(xive, pir); if (tctx) { xive_tctx_tm_write(NULL, tctx, offset, val, size); } diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 24e67020dbf..5b0b4d96242 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -301,23 +301,25 @@ void icp_reset(ICPState *icp) static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); + PowerPCCPU *cpu; CPUPPCState *env; Error *err = NULL; assert(icp->xics); assert(icp->cs); - env = &POWERPC_CPU(icp->cs)->env; + cpu = POWERPC_CPU(icp->cs); + env = &cpu->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER7: - icp->output = env->irq_inputs[POWER7_INPUT_INT]; + icp->output = qdev_get_gpio_in(DEVICE(cpu), POWER7_INPUT_INT); break; case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */ - icp->output = env->irq_inputs[POWER9_INPUT_INT]; + icp->output = qdev_get_gpio_in(DEVICE(cpu), POWER9_INPUT_INT); break; case PPC_FLAGS_INPUT_970: - icp->output = env->irq_inputs[PPC970_INPUT_INT]; + icp->output = qdev_get_gpio_in(DEVICE(cpu), PPC970_INPUT_INT); break; default: diff --git a/hw/intc/xive.c b/hw/intc/xive.c index ae221fed732..a986b96843e 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -695,8 +695,8 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp) env = &cpu->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER9: - tctx->hv_output = env->irq_inputs[POWER9_INPUT_HINT]; - tctx->os_output = env->irq_inputs[POWER9_INPUT_INT]; + tctx->hv_output = qdev_get_gpio_in(DEVICE(cpu), POWER9_INPUT_HINT); + tctx->os_output = qdev_get_gpio_in(DEVICE(cpu), POWER9_INPUT_INT); break; default: diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 6388558f92d..48f9ab10964 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -138,7 +138,20 @@ static void piix3_write_config(PCIDevice *dev, static void piix3_write_config_xen(PCIDevice *dev, uint32_t address, uint32_t val, int len) { - xen_piix_pci_write_config_client(address, val, len); + int i; + + /* Scan for updates to PCI link routes (0x60-0x63). */ + for (i = 0; i < len; i++) { + uint8_t v = (val >> (8 * i)) & 0xff; + if (v & 0x80) { + v = 0; + } + v &= 0xf; + if (((address + i) >= PIIX_PIRQCA) && ((address + i) <= PIIX_PIRQCD)) { + xen_set_pci_link_route(address + i - PIIX_PIRQCA, v); + } + } + piix3_write_config(dev, address, val, len); } diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 35b66807729..a99aa387c33 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -14,3 +14,6 @@ config LOONGARCH_VIRT select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI select LS7A_RTC + select SMBIOS + select ACPI_PCI + select ACPI_HW_REDUCED diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c new file mode 100644 index 00000000000..b95b83b0794 --- /dev/null +++ b/hw/loongarch/acpi-build.c @@ -0,0 +1,609 @@ +/* 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/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/aml-build.h" + +#include "hw/acpi/utils.h" +#include "hw/acpi/pci.h" + +#include "qom/qom-qobject.h" + +#include "hw/acpi/generic_event_device.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); + int i; + 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 < ms->smp.cpus; i++) { + /* Processor Core Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 17, 1); /* Type */ + build_append_int_noprefix(table_data, 15, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, i + 1, 4); /* ACPI Processor ID */ + build_append_int_noprefix(table_data, i, 4); /* Core ID */ + build_append_int_noprefix(table_data, 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, LS7A_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, LS7A_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) +{ + uint64_t i; + LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + MachineState *ms = MACHINE(lams); + AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, + .oem_table_id = lams->oem_table_id }; + + acpi_table_begin(&table, table_data); + build_append_int_noprefix(table_data, 1, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); /* Reserved */ + + for (i = 0; i < ms->smp.cpus; ++i) { + /* Processor Local APIC/SAPIC Affinity Structure */ + build_append_int_noprefix(table_data, 0, 1); /* Type */ + build_append_int_noprefix(table_data, 16, 1); /* Length */ + /* Proximity Domain [7:0] */ + build_append_int_noprefix(table_data, 0, 1); + build_append_int_noprefix(table_data, i, 1); /* APIC ID */ + /* 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 */ + } + + build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, + 0, MEM_AFFINITY_ENABLED); + + build_srat_memory(table_data, VIRT_HIGHMEM_BASE, machine->ram_size - VIRT_LOWMEM_SIZE, + 0, 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_gpex_pci0_int(Aml *table) +{ + Aml *sb_scope = aml_scope("_SB"); + Aml *pci0_scope = aml_scope("PCI0"); + Aml *prt_pkg = aml_varpackage(128); + int slot, pin; + + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + Aml *pkg = aml_package(4); + aml_append(pkg, aml_int((slot << 16) | 0xFFFF)); + aml_append(pkg, aml_int(pin)); + aml_append(pkg, aml_int(0)); + aml_append(pkg, aml_int(80 + (slot + pin) % 4)); + aml_append(prt_pkg, pkg); + } + } + aml_append(pci0_scope, aml_name_decl("_PRT", prt_pkg)); + aml_append(sb_scope, pci0_scope); + aml_append(table, sb_scope); +} + +static void build_dbg_aml(Aml *table) +{ + Aml *field; + Aml *method; + Aml *while_ctx; + Aml *scope = aml_scope("\\"); + Aml *buf = aml_local(0); + Aml *len = aml_local(1); + Aml *idx = aml_local(2); + + aml_append(scope, + aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01)); + field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("DBGB", 8)); + aml_append(scope, field); + + method = aml_method("DBUG", 1, AML_NOTSERIALIZED); + + aml_append(method, aml_to_hexstring(aml_arg(0), buf)); + aml_append(method, aml_to_buffer(buf, buf)); + aml_append(method, aml_subtract(aml_sizeof(buf), aml_int(1), len)); + aml_append(method, aml_store(aml_int(0), idx)); + + while_ctx = aml_while(aml_lless(idx, len)); + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(buf, idx)), aml_name("DBGB"))); + aml_append(while_ctx, aml_increment(idx)); + aml_append(method, while_ctx); + aml_append(method, aml_store(aml_int(0x0A), aml_name("DBGB"))); + aml_append(scope, method); + aml_append(table, scope); +} + +static Aml *build_osc_method(void) +{ + Aml *if_ctx; + Aml *if_ctx2; + Aml *else_ctx; + Aml *method; + Aml *a_cwd1 = aml_name("CDW1"); + Aml *a_ctrl = aml_local(0); + + method = aml_method("_OSC", 4, AML_NOTSERIALIZED); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); + + if_ctx = aml_if(aml_equal( + aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); + aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); + + /* + * Always allow native PME, AER (no dependencies) + * Allow SHPC (PCI bridges can have SHPC controller) + */ + aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1F), a_ctrl)); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); + /* Unknown revision */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); + /* Capabilities bits were masked */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + /* Update DWORD3 in the buffer */ + aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); + aml_append(method, if_ctx); + + else_ctx = aml_else(); + /* Unrecognized UUID */ + aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); + aml_append(method, else_ctx); + + aml_append(method, aml_return(aml_arg(3))); + return method; +} + +static void build_uart_device_aml(Aml *table) +{ + Aml *dev; + Aml *crs; + Aml *pkg0, *pkg1, *pkg2; + uint32_t uart_irq = LS7A_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, 0x1FE001E0, 0x1FE001E7, 0, 0x8)); + 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); +} + +/* build DSDT */ +static void +build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + Aml *dsdt, *sb_scope, *scope, *dev, *crs, *pkg; + int root_bus_limit = 0x7F; + 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_dbg_aml(dsdt); + + sb_scope = aml_scope("_SB"); + dev = aml_device("PCI0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); + aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); + aml_append(dev, aml_name_decl("_ADR", aml_int(0))); + aml_append(dev, aml_name_decl("_BBN", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(1))); + aml_append(dev, build_osc_method()); + aml_append(sb_scope, dev); + aml_append(dsdt, sb_scope); + + build_gpex_pci0_int(dsdt); + build_uart_device_aml(dsdt); + if (lams->acpi_ged) { + build_ged_aml(dsdt, "\\_SB."GED_DEVICE, + HOTPLUG_HANDLER(lams->acpi_ged), + LS7A_SCI_IRQ - PCH_PIC_IRQ_OFFSET, AML_SYSTEM_MEMORY, + VIRT_GED_EVT_ADDR); + } + + scope = aml_scope("\\_SB.PCI0"); + /* Build PCI0._CRS */ + crs = aml_resource_template(); + aml_append(crs, + aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, + 0x0000, 0x0, root_bus_limit, + 0x0000, root_bus_limit + 1)); + aml_append(crs, + aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0x0000, 0x0000, 0xFFFF, 0x18000000, 0x10000)); + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_WRITE, + 0, LS7A_PCI_MEM_BASE, + LS7A_PCI_MEM_BASE + LS7A_PCI_MEM_SIZE - 1, + 0, LS7A_PCI_MEM_BASE)); + aml_append(scope, aml_name_decl("_CRS", crs)); + aml_append(dsdt, scope); + + /* 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, fadt, dsdt; + uint8_t *u; + size_t aml_len = 0; + 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); + + /* + * Count the size of the DSDT, 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); + 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); + aml_len += tables_blob->len - fadt; + + acpi_add_table(table_offsets, tables_blob); + build_madt(tables_blob, tables->linker, lams); + + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, machine); + + acpi_add_table(table_offsets, tables_blob); + { + AcpiMcfgInfo mcfg = { + .base = cpu_to_le64(LS_PCIECFG_BASE), + .size = cpu_to_le64(LS_PCIECFG_SIZE), + }; + build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id, + lams->oem_table_id); + } + + /* 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/fw_cfg.c b/hw/loongarch/fw_cfg.c new file mode 100644 index 00000000000..f6503d56072 --- /dev/null +++ b/hw/loongarch/fw_cfg.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU fw_cfg helpers (LoongArch specific) + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/loongarch/fw_cfg.h" +#include "hw/loongarch/virt.h" +#include "hw/nvram/fw_cfg.h" +#include "sysemu/sysemu.h" + +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) +{ + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); +} + +FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) +{ + FWCfgState *fw_cfg; + int max_cpus = ms->smp.max_cpus; + int smp_cpus = ms->smp.cpus; + + fw_cfg = fw_cfg_init_mem_wide(VIRT_FWCFG_BASE + 8, VIRT_FWCFG_BASE, 8, 0, NULL); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); + + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + return fw_cfg; +} diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h new file mode 100644 index 00000000000..7c0de4db4ad --- /dev/null +++ b/hw/loongarch/fw_cfg.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU fw_cfg helpers (LoongArch specific) + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_FW_CFG_H +#define HW_LOONGARCH_FW_CFG_H + +#include "hw/boards.h" +#include "hw/nvram/fw_cfg.h" + +FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); +#endif diff --git a/hw/loongarch/loongson3.c b/hw/loongarch/loongson3.c index bd20ebbb78b..a08dc9d2995 100644 --- a/hw/loongarch/loongson3.c +++ b/hw/loongarch/loongson3.c @@ -28,13 +28,201 @@ #include "hw/pci-host/ls7a.h" #include "hw/pci-host/gpex.h" #include "hw/misc/unimp.h" - +#include "hw/loongarch/fw_cfg.h" #include "target/loongarch/cpu.h" +#include "hw/firmware/smbios.h" +#include "hw/acpi/aml-build.h" +#include "qapi/qapi-visit-common.h" +#include "hw/acpi/generic_event_device.h" +#include "hw/mem/nvdimm.h" +#include "sysemu/device_tree.h" +#include + +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); +} + +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)); + + 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); + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + g_free(nodename); + } + + /*cpu map */ + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + + for (num = smp_cpus - 1; num >= 0; num--) { + char *cpu_path = g_strdup_printf("/cpus/cpu@%d", num); + char *map_path; + + if (ms->smp.threads > 1) { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d/thread%d", + num / (ms->smp.cores * ms->smp.threads), + (num / ms->smp.threads) % ms->smp.cores, + num % ms->smp.threads); + } else { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d", + num / ms->smp.cores, + num % ms->smp.cores); + } + qemu_fdt_add_path(ms->fdt, map_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", cpu_path); + + g_free(map_path); + g_free(cpu_path); + } +} + +static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) +{ + char *nodename; + hwaddr base = VIRT_FWCFG_BASE; + const MachineState *ms = MACHINE(lams); + + nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "qemu,fw-cfg-mmio"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base, 2, 0x8); + 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 = LS7A_PCI_MEM_BASE; + hwaddr size_mmio = LS7A_PCI_MEM_SIZE; + hwaddr base_pio = LS7A_PCI_IO_BASE; + hwaddr size_pio = LS7A_PCI_IO_SIZE; + hwaddr base_pcie = LS_PCIECFG_BASE; + hwaddr size_pcie = LS_PCIECFG_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(LS_PCIECFG_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, LS7A_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); + qemu_fdt_dumpdtb(ms->fdt, lams->fdt_size); +} + #define PM_BASE 0x10080000 #define PM_SIZE 0x100 #define PM_CTRL 0x10 +static void virt_build_smbios(LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + MachineClass *mc = MACHINE_GET_CLASS(lams); + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + const char *product = "QEMU Virtual Machine"; + + if (!lams->fw_cfg) { + return; + } + + smbios_set_defaults("QEMU", product, mc->name, false, + true, SMBIOS_ENTRY_POINT_TYPE_64); + + smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len, &error_fatal); + + if (smbios_anchor) { + fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } +} + +static void virt_machine_done(Notifier *notifier, void *data) +{ + LoongArchMachineState *lams = container_of(notifier, + LoongArchMachineState, machine_done); + virt_build_smbios(lams); + loongarch_acpi_setup(lams); +} + +struct memmap_entry { + uint64_t address; + uint64_t length; + uint32_t type; + uint32_t reserved; +}; + +static struct memmap_entry *memmap_table; +static unsigned memmap_entries; + +static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) +{ + /* Ensure there are no duplicate entries. */ + for (unsigned i = 0; i < memmap_entries; i++) { + assert(memmap_table[i].address != address); + } + + memmap_table = g_renew(struct memmap_entry, memmap_table, + memmap_entries + 1); + memmap_table[memmap_entries].address = cpu_to_le64(address); + memmap_table[memmap_entries].length = cpu_to_le64(length); + memmap_table[memmap_entries].type = cpu_to_le32(type); + memmap_table[memmap_entries].reserved = 0; + memmap_entries++; +} + /* * This is a placeholder for missing ACPI, * and will eventually be replaced. @@ -76,6 +264,8 @@ static const MemoryRegionOps loongarch_virt_pm_ops = { 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) @@ -103,7 +293,32 @@ static int64_t load_kernel_info(void) return kernel_entry; } -static void loongarch_devices_init(DeviceState *pch_pic) +static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams) +{ + DeviceState *dev; + MachineState *ms = MACHINE(lams); + uint32_t event = ACPI_GED_PWR_DOWN_EVT; + + if (ms->ram_slots) { + event |= ACPI_GED_MEM_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); + qdev_prop_set_uint32(dev, "ged-event", event); + + /* ged event */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, VIRT_GED_EVT_ADDR); + /* memory hotplug */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, VIRT_GED_MEM_ADDR); + /* ged regs used for reset and power down */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(pch_pic, LS7A_SCI_IRQ - PCH_PIC_IRQ_OFFSET)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + return dev; +} + +static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams) { DeviceState *gpex_dev; SysBusDevice *d; @@ -179,6 +394,8 @@ static void loongarch_devices_init(DeviceState *pch_pic) 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); } static void loongarch_irq_init(LoongArchMachineState *lams) @@ -230,7 +447,10 @@ static void loongarch_irq_init(LoongArchMachineState *lams) /* IPI iocsr memory region */ memory_region_add_subregion(&env->system_iocsr, SMP_IPI_MAILBOX, sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), - cpu)); + cpu * 2)); + memory_region_add_subregion(&env->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), + cpu * 2 + 1)); /* extioi iocsr memory region */ memory_region_add_subregion(&env->system_iocsr, APIC_BASE, sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), @@ -267,6 +487,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) } pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); + qdev_prop_set_uint32(pch_msi, "msi_irq_base", PCH_MSI_IRQ_START); d = SYS_BUS_DEVICE(pch_msi); sysbus_realize_and_unref(d, &error_fatal); sysbus_mmio_map(d, 0, LS7A_PCH_MSI_ADDR_LOW); @@ -276,7 +497,38 @@ static void loongarch_irq_init(LoongArchMachineState *lams) qdev_get_gpio_in(extioi, i + PCH_MSI_IRQ_START)); } - loongarch_devices_init(pch_pic); + loongarch_devices_init(pch_pic, lams); +} + +static void loongarch_firmware_init(LoongArchMachineState *lams) +{ + char *filename = MACHINE(lams)->firmware; + char *bios_name = NULL; + int bios_size; + + lams->bios_loaded = false; + if (filename) { + bios_name = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); + if (!bios_name) { + error_report("Could not find ROM image '%s'", filename); + exit(1); + } + + bios_size = load_image_targphys(bios_name, VIRT_BIOS_BASE, VIRT_BIOS_SIZE); + if (bios_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + + g_free(bios_name); + + memory_region_init_ram(&lams->bios, NULL, "loongarch.bios", + VIRT_BIOS_SIZE, &error_fatal); + memory_region_set_readonly(&lams->bios, true); + memory_region_add_subregion(get_system_memory(), VIRT_BIOS_BASE, &lams->bios); + lams->bios_loaded = true; + } + } static void reset_load_elf(void *opaque) @@ -290,18 +542,97 @@ static void reset_load_elf(void *opaque) } } +/* Load an image file into an fw_cfg entry identified by key. */ +static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, + uint16_t data_key, const char *image_name, + bool try_decompress) +{ + size_t size = -1; + uint8_t *data; + + if (image_name == NULL) { + return; + } + + if (try_decompress) { + size = load_image_gzipped_buffer(image_name, + LOAD_IMAGE_MAX_GUNZIP_BYTES, &data); + } + + if (size == (size_t)-1) { + gchar *contents; + gsize length; + + if (!g_file_get_contents(image_name, &contents, &length, NULL)) { + error_report("failed to load \"%s\"", image_name); + exit(1); + } + size = length; + data = (uint8_t *)contents; + } + + fw_cfg_add_i32(fw_cfg, size_key, size); + fw_cfg_add_bytes(fw_cfg, data_key, data, size); +} + +static void fw_cfg_add_kernel_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, + loaderparams.kernel_filename, + false); + + if (loaderparams.initrd_filename) { + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + loaderparams.initrd_filename, false); + } + + if (loaderparams.kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(loaderparams.kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + loaderparams.kernel_cmdline); + } +} + +static void loongarch_firmware_boot(LoongArchMachineState *lams) +{ + fw_cfg_add_kernel_info(lams->fw_cfg); +} + +static void loongarch_direct_kernel_boot(LoongArchMachineState *lams) +{ + MachineState *machine = MACHINE(lams); + int64_t kernel_addr = 0; + LoongArchCPU *lacpu; + int i; + + kernel_addr = load_kernel_info(); + if (!machine->firmware) { + for (i = 0; i < machine->smp.cpus; i++) { + lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); + lacpu->env.load_elf = true; + lacpu->env.elf_address = kernel_addr; + } + } +} + static void loongarch_init(MachineState *machine) { + LoongArchCPU *lacpu; const char *cpu_model = machine->cpu_type; - const char *kernel_filename = machine->kernel_filename; ram_addr_t offset = 0; ram_addr_t ram_size = machine->ram_size; uint64_t highram_size = 0; MemoryRegion *address_space_mem = get_system_memory(); LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - LoongArchCPU *lacpu; int i; - int64_t kernel_addr = 0; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -316,41 +647,102 @@ static void loongarch_init(MachineState *machine) error_report("ram_size must be greater than 1G."); exit(1); } - + create_fdt(lams); /* Init CPUs */ for (i = 0; i < machine->smp.cpus; i++) { cpu_create(machine->cpu_type); } - + fdt_add_cpu_nodes(lams); /* Add memory region */ memory_region_init_alias(&lams->lowmem, NULL, "loongarch.lowram", machine->ram, 0, 256 * MiB); memory_region_add_subregion(address_space_mem, offset, &lams->lowmem); offset += 256 * MiB; + memmap_add_entry(0, 256 * MiB, 1); highram_size = ram_size - 256 * MiB; memory_region_init_alias(&lams->highmem, NULL, "loongarch.highmem", machine->ram, offset, highram_size); memory_region_add_subregion(address_space_mem, 0x90000000, &lams->highmem); + memmap_add_entry(0x90000000, highram_size, 1); /* Add isa io region */ memory_region_init_alias(&lams->isa_io, NULL, "isa-io", get_system_io(), 0, LOONGARCH_ISA_IO_SIZE); memory_region_add_subregion(address_space_mem, LOONGARCH_ISA_IO_BASE, &lams->isa_io); - if (kernel_filename) { - loaderparams.ram_size = ram_size; - loaderparams.kernel_filename = kernel_filename; - kernel_addr = load_kernel_info(); - if (!machine->firmware) { - for (i = 0; i < machine->smp.cpus; i++) { - lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); - lacpu->env.load_elf = true; - lacpu->env.elf_address = kernel_addr; - qemu_register_reset(reset_load_elf, lacpu); - } + /* load the BIOS image. */ + loongarch_firmware_init(lams); + + /* fw_cfg init */ + lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine); + rom_set_fw(lams->fw_cfg); + if (lams->fw_cfg != NULL) { + fw_cfg_add_file(lams->fw_cfg, "etc/memmap", + memmap_table, + sizeof(struct memmap_entry) * (memmap_entries)); + } + fdt_add_fw_cfg_node(lams); + loaderparams.ram_size = ram_size; + loaderparams.kernel_filename = machine->kernel_filename; + loaderparams.kernel_cmdline = machine->kernel_cmdline; + loaderparams.initrd_filename = machine->initrd_filename; + /* load the kernel. */ + if (loaderparams.kernel_filename) { + if (lams->bios_loaded) { + loongarch_firmware_boot(lams); + } else { + loongarch_direct_kernel_boot(lams); } } + /* register reset function */ + for (i = 0; i < machine->smp.cpus; i++) { + lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); + qemu_register_reset(reset_load_elf, lacpu); + } /* Initialize the IO interrupt subsystem */ loongarch_irq_init(lams); + lams->machine_done.notify = virt_machine_done; + qemu_add_machine_init_done_notifier(&lams->machine_done); + fdt_add_pcie_node(lams); + + /* load fdt */ + MemoryRegion *fdt_rom = g_new(MemoryRegion, 1); + memory_region_init_rom(fdt_rom, NULL, "fdt", LA_FDT_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), LA_FDT_BASE, fdt_rom); + rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, LA_FDT_BASE); +} + +bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) +{ + if (lams->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + OnOffAuto acpi = lams->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); +} + +static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lams->acpi, errp); +} + +static void loongarch_machine_initfn(Object *obj) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + + lams->acpi = ON_OFF_AUTO_AUTO; + lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); + lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); } static void loongarch_class_init(ObjectClass *oc, void *data) @@ -368,6 +760,12 @@ static void loongarch_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; mc->no_cdrom = 1; + + object_class_property_add(oc, "acpi", "OnOffAuto", + loongarch_get_acpi, loongarch_set_acpi, + NULL, NULL); + object_class_property_set_description(oc, "acpi", + "Enable ACPI"); } static const TypeInfo loongarch_machine_types[] = { @@ -376,6 +774,7 @@ static const TypeInfo loongarch_machine_types[] = { .parent = TYPE_MACHINE, .instance_size = sizeof(LoongArchMachineState), .class_init = loongarch_class_init, + .instance_init = loongarch_machine_initfn, } }; diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index cecb1a5d653..6a2a1b18e58 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -1,4 +1,8 @@ loongarch_ss = ss.source_set() -loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('loongson3.c')) +loongarch_ss.add(files( + 'fw_cfg.c', +)) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('loongson3.c'), fdt]) +loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h index adbf0c5521e..bd8b212fd35 100644 --- a/hw/m68k/bootinfo.h +++ b/hw/m68k/bootinfo.h @@ -54,6 +54,22 @@ stb_phys(as, base++, string[i]); \ } \ stb_phys(as, base++, 0); \ - base = (parameters_base + 1) & ~1; \ + base = (base + 1) & ~1; \ + } while (0) + +#define BOOTINFODATA(as, base, id, data, len) \ + do { \ + int i; \ + stw_phys(as, base, id); \ + base += 2; \ + stw_phys(as, base, \ + (sizeof(struct bi_record) + len + 3) & ~1); \ + base += 2; \ + stw_phys(as, base, len); \ + base += 2; \ + for (i = 0; i < len; ++i) { \ + stb_phys(as, base++, data[i]); \ + } \ + base = (base + 1) & ~1; \ } while (0) #endif diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 099a758c6f6..101ab0f803f 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -686,6 +686,21 @@ static void q800_init(MachineState *machine) } } +static GlobalProperty hw_compat_q800[] = { + { "scsi-hd", "quirk_mode_page_vendor_specific_apple", "on"}, + { "scsi-hd", "vendor", " SEAGATE" }, + { "scsi-hd", "product", " ST225N" }, + { "scsi-hd", "ver", "1.0 " }, + { "scsi-cd", "quirk_mode_page_apple_vendor", "on"}, + { "scsi-cd", "quirk_mode_sense_rom_use_dbd", "on"}, + { "scsi-cd", "quirk_mode_page_vendor_specific_apple", "on"}, + { "scsi-cd", "quirk_mode_page_truncated", "on"}, + { "scsi-cd", "vendor", "MATSHITA" }, + { "scsi-cd", "product", "CD-ROM CR-8005" }, + { "scsi-cd", "ver", "1.0k" }, +}; +static const size_t hw_compat_q800_len = G_N_ELEMENTS(hw_compat_q800); + static void q800_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -695,6 +710,7 @@ static void q800_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->block_default_type = IF_SCSI; mc->default_ram_id = "m68k_mac.ram"; + compat_props_add(mc->compat_props, hw_compat_q800, hw_compat_q800_len); } static const TypeInfo q800_machine_typeinfo = { diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index e215aa3d421..0aa383fa6bd 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/guest-random.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "hw/boards.h" @@ -120,6 +121,7 @@ static void virt_init(MachineState *machine) hwaddr io_base; int i; ResetInfo *reset_info; + uint8_t rng_seed[32]; if (ram_size > 3399672 * KiB) { /* @@ -245,6 +247,11 @@ static void virt_init(MachineState *machine) kernel_cmdline); } + /* Pass seed to RNG. */ + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + BOOTINFODATA(cs->as, parameters_base, BI_VIRT_RNG_SEED, + rng_seed, sizeof(rng_seed)); + /* load initrd */ if (initrd_filename) { initrd_size = get_image_size(initrd_filename); diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 96dc6ab32dd..6aefe9a61b9 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -136,11 +136,11 @@ static void mips_jazz_init(MachineState *machine, MemoryRegion *isa_mem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); MemoryRegion *rtc = g_new(MemoryRegion, 1); - MemoryRegion *i8042 = g_new(MemoryRegion, 1); MemoryRegion *dma_dummy = g_new(MemoryRegion, 1); MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1); NICInfo *nd; DeviceState *dev, *rc4030; + MMIOKBDState *i8042; SysBusDevice *sysbus; ISABus *isa_bus; ISADevice *pit; @@ -361,9 +361,19 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0x80004000, rtc); /* Keyboard (i8042) */ - i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7), - i8042, 0x1000, 0x1); - memory_region_add_subregion(address_space, 0x80005000, i8042); + i8042 = I8042_MMIO(qdev_new(TYPE_I8042_MMIO)); + qdev_prop_set_uint64(DEVICE(i8042), "mask", 1); + qdev_prop_set_uint32(DEVICE(i8042), "size", 0x1000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(i8042), &error_fatal); + + qdev_connect_gpio_out(DEVICE(i8042), I8042_KBD_IRQ, + qdev_get_gpio_in(rc4030, 6)); + qdev_connect_gpio_out(DEVICE(i8042), I8042_MOUSE_IRQ, + qdev_get_gpio_in(rc4030, 7)); + + memory_region_add_subregion(address_space, 0x80005000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(i8042), + 0)); /* Serial ports */ serial_mm_init(address_space, 0x80006000, 0, diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 4b5997e18fa..ac21be306c6 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -338,14 +338,14 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, int algo; data &= ahc->hash_mask; - if ((data & HASH_HMAC_MASK)) { + if ((data & HASH_DIGEST_HMAC)) { qemu_log_mask(LOG_UNIMP, - "%s: HMAC engine command mode %"PRIx64" not implemented", - __func__, (data & HASH_HMAC_MASK) >> 8); + "%s: HMAC mode not implemented\n", + __func__); } if (data & BIT(1)) { qemu_log_mask(LOG_UNIMP, - "%s: Cascaded mode not implemented", + "%s: Cascaded mode not implemented\n", __func__); } algo = hash_algo_lookup(data); diff --git a/hw/misc/aspeed_peci.c b/hw/misc/aspeed_peci.c new file mode 100644 index 00000000000..93cc672e968 --- /dev/null +++ b/hw/misc/aspeed_peci.c @@ -0,0 +1,152 @@ +/* + * Aspeed PECI Controller + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * This code is licensed under the GPL version 2 or later. See the COPYING + * file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/misc/aspeed_peci.h" +#include "hw/registerfields.h" +#include "trace.h" + +#define ASPEED_PECI_CC_RSP_SUCCESS (0x40U) + +/* Command Register */ +REG32(PECI_CMD, 0x08) + FIELD(PECI_CMD, FIRE, 0, 1) + +/* Interrupt Control Register */ +REG32(PECI_INT_CTRL, 0x18) + +/* Interrupt Status Register */ +REG32(PECI_INT_STS, 0x1C) + FIELD(PECI_INT_STS, CMD_DONE, 0, 1) + +/* Rx/Tx Data Buffer Registers */ +REG32(PECI_WR_DATA0, 0x20) +REG32(PECI_RD_DATA0, 0x30) + +static void aspeed_peci_raise_interrupt(AspeedPECIState *s, uint32_t status) +{ + trace_aspeed_peci_raise_interrupt(s->regs[R_PECI_INT_CTRL], status); + + s->regs[R_PECI_INT_STS] = s->regs[R_PECI_INT_CTRL] & status; + if (!s->regs[R_PECI_INT_STS]) { + return; + } + qemu_irq_raise(s->irq); +} + +static uint64_t aspeed_peci_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedPECIState *s = ASPEED_PECI(opaque); + uint64_t data; + + if (offset >= ASPEED_PECI_NR_REGS << 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + data = s->regs[offset >> 2]; + + trace_aspeed_peci_read(offset, data); + return data; +} + +static void aspeed_peci_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedPECIState *s = ASPEED_PECI(opaque); + + trace_aspeed_peci_write(offset, data); + + if (offset >= ASPEED_PECI_NR_REGS << 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + switch (offset) { + case A_PECI_INT_STS: + s->regs[R_PECI_INT_STS] &= ~data; + if (!s->regs[R_PECI_INT_STS]) { + qemu_irq_lower(s->irq); + } + break; + case A_PECI_CMD: + /* + * Only the FIRE bit is writable. Once the command is complete, it + * should be cleared. Since we complete the command immediately, the + * value is not stored in the register array. + */ + if (!FIELD_EX32(data, PECI_CMD, FIRE)) { + break; + } + if (s->regs[R_PECI_INT_STS]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Interrupt status must be " + "cleared before firing another command: 0x%08x\n", + __func__, s->regs[R_PECI_INT_STS]); + break; + } + s->regs[R_PECI_RD_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS; + s->regs[R_PECI_WR_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS; + aspeed_peci_raise_interrupt(s, + FIELD_DP32(0, PECI_INT_STS, CMD_DONE, 1)); + break; + default: + s->regs[offset / sizeof(s->regs[0])] = data; + break; + } +} + +static const MemoryRegionOps aspeed_peci_ops = { + .read = aspeed_peci_read, + .write = aspeed_peci_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void aspeed_peci_realize(DeviceState *dev, Error **errp) +{ + AspeedPECIState *s = ASPEED_PECI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_peci_ops, s, + TYPE_ASPEED_PECI, 0x1000); + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_irq(sbd, &s->irq); +} + +static void aspeed_peci_reset(DeviceState *dev) +{ + AspeedPECIState *s = ASPEED_PECI(dev); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static void aspeed_peci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_peci_realize; + dc->reset = aspeed_peci_reset; + dc->desc = "Aspeed PECI Controller"; +} + +static const TypeInfo aspeed_peci_types[] = { + { + .name = TYPE_ASPEED_PECI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedPECIState), + .class_init = aspeed_peci_class_init, + .abstract = false, + }, +}; + +DEFINE_TYPES(aspeed_peci_types); diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index bfa8b81d01c..c6f328e3be2 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" +#include "hw/qdev-properties.h" #include "hw/misc/aspeed_sbc.h" #include "qapi/error.h" #include "migration/vmstate.h" @@ -19,6 +20,27 @@ #define R_STATUS (0x014 / 4) #define R_QSR (0x040 / 4) +/* R_STATUS */ +#define ABR_EN BIT(14) /* Mirrors SCU510[11] */ +#define ABR_IMAGE_SOURCE BIT(13) +#define SPI_ABR_IMAGE_SOURCE BIT(12) +#define SB_CRYPTO_KEY_EXP_DONE BIT(11) +#define SB_CRYPTO_BUSY BIT(10) +#define OTP_WP_EN BIT(9) +#define OTP_ADDR_WP_EN BIT(8) +#define LOW_SEC_KEY_EN BIT(7) +#define SECURE_BOOT_EN BIT(6) +#define UART_BOOT_EN BIT(5) +/* bit 4 reserved*/ +#define OTP_CHARGE_PUMP_READY BIT(3) +#define OTP_IDLE BIT(2) +#define OTP_MEM_IDLE BIT(1) +#define OTP_COMPARE_STATUS BIT(0) + +/* QSR */ +#define QSR_RSA_MASK (0x3 << 12) +#define QSR_HASH_MASK (0x3 << 10) + static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedSBCState *s = ASPEED_SBC(opaque); @@ -80,8 +102,17 @@ static void aspeed_sbc_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); /* Set secure boot enabled with RSA4096_SHA256 and enable eMMC ABR */ - s->regs[R_STATUS] = 0x000044C6; - s->regs[R_QSR] = 0x07C07C89; + s->regs[R_STATUS] = OTP_IDLE | OTP_MEM_IDLE; + + if (s->emmc_abr) { + s->regs[R_STATUS] &= ABR_EN; + } + + if (s->signing_settings) { + s->regs[R_STATUS] &= SECURE_BOOT_EN; + } + + s->regs[R_QSR] = s->signing_settings; } static void aspeed_sbc_realize(DeviceState *dev, Error **errp) @@ -105,6 +136,12 @@ static const VMStateDescription vmstate_aspeed_sbc = { } }; +static Property aspeed_sbc_properties[] = { + DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), + DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void aspeed_sbc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -112,6 +149,7 @@ static void aspeed_sbc_class_init(ObjectClass *klass, void *data) dc->realize = aspeed_sbc_realize; dc->reset = aspeed_sbc_reset; dc->vmsd = &vmstate_aspeed_sbc; + device_class_set_props(dc, aspeed_sbc_properties); } static const TypeInfo aspeed_sbc_info = { diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 19b03471fc4..83353649064 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -270,6 +270,7 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) break; } + trace_aspeed_scu_read(offset, size, s->regs[reg]); return s->regs[reg]; } @@ -637,6 +638,7 @@ static uint64_t aspeed_ast2600_scu_read(void *opaque, hwaddr offset, break; } + trace_aspeed_scu_read(offset, size, s->regs[reg]); return s->regs[reg]; } diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 525e38ce932..fba85a53d7d 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -351,7 +351,7 @@ static void via1_one_second(void *opaque) static void pram_update(MOS6522Q800VIA1State *v1s) { if (v1s->blk) { - if (blk_pwrite(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM), 0) < 0) { + if (blk_pwrite(v1s->blk, 0, sizeof(v1s->PRAM), v1s->PRAM, 0) < 0) { qemu_log("pram_update: cannot write to file\n"); } } @@ -1029,8 +1029,8 @@ static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp) return; } - len = blk_pread(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM)); - if (len != sizeof(v1s->PRAM)) { + ret = blk_pread(v1s->blk, 0, sizeof(v1s->PRAM), v1s->PRAM, 0); + if (ret < 0) { error_setg(errp, "can't read PRAM contents"); return; } diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 132b7b7344b..95268eddc07 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -116,7 +116,8 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_scu.c', 'aspeed_sbc.c', 'aspeed_sdmc.c', - 'aspeed_xdma.c')) + 'aspeed_xdma.c', + 'aspeed_peci.c')) softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index 6d5f84e6c20..6d7fdb040a5 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -64,8 +64,8 @@ static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size) if (s->blk) { int32_t buf; - if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, &buf, - SIFIVE_U_OTP_FUSE_WORD) < 0) { + if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, + SIFIVE_U_OTP_FUSE_WORD, &buf, 0) < 0) { error_report("read error index<%d>", s->pa); return 0xff; } @@ -167,8 +167,8 @@ static void sifive_u_otp_write(void *opaque, hwaddr addr, /* write to backend */ if (s->blk) { if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, - &s->fuse[s->pa], SIFIVE_U_OTP_FUSE_WORD, - 0) < 0) { + SIFIVE_U_OTP_FUSE_WORD, &s->fuse[s->pa], 0) + < 0) { error_report("write error index<%d>", s->pa); } } @@ -240,7 +240,7 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) return; } - if (blk_pread(s->blk, 0, s->fuse, filesize) != filesize) { + if (blk_pread(s->blk, 0, filesize, s->fuse, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } @@ -261,14 +261,14 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) serial_data = s->serial; if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD, - &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) { + SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) { error_setg(errp, "failed to write index<%d>", index); return; } serial_data = ~(s->serial); if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD, - &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) { + SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) { error_setg(errp, "failed to write index<%d>", index + 1); return; } diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c5e37b01547..4d51a80de1d 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -69,6 +69,7 @@ slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x" # aspeed_scu.c aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 # mps2-scc.c mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" @@ -209,6 +210,11 @@ aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C aspeed_sdmc_write(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 aspeed_sdmc_read(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 +# aspeed_peci.c +aspeed_peci_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_peci_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_peci_raise_interrupt(uint32_t ctrl, uint32_t status) "ctrl 0x%" PRIx32 " status 0x%" PRIx32 + # bcm2835_property.c bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" diff --git a/hw/net/e1000.c b/hw/net/e1000.c index f5bc81296d1..e26e0a64c17 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -979,7 +979,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH]; pci_dma_read(d, base, &desc, sizeof(desc)); desc.special = vlan_special; - desc.status |= (vlan_status | E1000_RXD_STAT_DD); + desc.status &= ~E1000_RXD_STAT_DD; if (desc.buffer_addr) { if (desc_offset < size) { size_t iov_copy; @@ -1013,6 +1013,9 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) DBGOUT(RX, "Null RX descriptor!!\n"); } pci_dma_write(d, base, &desc, sizeof(desc)); + desc.status |= (vlan_status | E1000_RXD_STAT_DD); + pci_dma_write(d, base + offsetof(struct e1000_rx_desc, status), + &desc.status, sizeof(desc.status)); if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 0db9aaf76a0..8c11b237de7 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -438,7 +438,7 @@ static void imx_eth_update(IMXFECState *s) * assignment fail. * * To ensure that all versions of Linux work, generate ENET_INT_MAC - * interrrupts on both interrupt lines. This should be changed if and when + * interrupts on both interrupt lines. This should be changed if and when * qemu supports IOMUX. */ if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7ad948ee7c8..dd0d056fde5 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -49,7 +49,6 @@ #define VIRTIO_NET_VM_VERSION 11 -#define MAC_TABLE_ENTRIES 64 #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ /* previously fixed value */ @@ -1434,57 +1433,71 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } -static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev, + const struct iovec *in_sg, unsigned in_num, + const struct iovec *out_sg, + unsigned out_num) { VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_ctrl_hdr ctrl; virtio_net_ctrl_ack status = VIRTIO_NET_ERR; - VirtQueueElement *elem; size_t s; struct iovec *iov, *iov2; - unsigned int iov_cnt; + + if (iov_size(in_sg, in_num) < sizeof(status) || + iov_size(out_sg, out_num) < sizeof(ctrl)) { + virtio_error(vdev, "virtio-net ctrl missing headers"); + return 0; + } + + iov2 = iov = g_memdup2(out_sg, sizeof(struct iovec) * out_num); + s = iov_to_buf(iov, out_num, 0, &ctrl, sizeof(ctrl)); + iov_discard_front(&iov, &out_num, sizeof(ctrl)); + if (s != sizeof(ctrl)) { + status = VIRTIO_NET_ERR; + } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { + status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { + status = virtio_net_handle_mac(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { + status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { + status = virtio_net_handle_announce(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { + status = virtio_net_handle_mq(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { + status = virtio_net_handle_offloads(n, ctrl.cmd, iov, out_num); + } + + s = iov_from_buf(in_sg, in_num, 0, &status, sizeof(status)); + assert(s == sizeof(status)); + + g_free(iov2); + return sizeof(status); +} + +static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtQueueElement *elem; for (;;) { + size_t written; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { break; } - if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || - iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { - virtio_error(vdev, "virtio-net ctrl missing headers"); + + written = virtio_net_handle_ctrl_iov(vdev, elem->in_sg, elem->in_num, + elem->out_sg, elem->out_num); + if (written > 0) { + virtqueue_push(vq, elem, written); + virtio_notify(vdev, vq); + g_free(elem); + } else { virtqueue_detach_element(vq, elem, 0); g_free(elem); break; } - - iov_cnt = elem->out_num; - iov2 = iov = g_memdup2(elem->out_sg, - sizeof(struct iovec) * elem->out_num); - s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); - iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); - if (s != sizeof(ctrl)) { - status = VIRTIO_NET_ERR; - } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { - status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { - status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { - status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { - status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { - status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { - status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt); - } - - s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); - assert(s == sizeof(status)); - - virtqueue_push(vq, elem, sizeof(status)); - virtio_notify(vdev, vq); - g_free(iov2); - g_free(elem); } } diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 1e6e0fcad91..533ad14e7a6 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -35,6 +35,11 @@ * mdts=,vsl=, \ * zoned.zasl=, \ * zoned.auto_transition=, \ + * sriov_max_vfs= \ + * sriov_vq_flexible= \ + * sriov_vi_flexible= \ + * sriov_max_vi_per_vf= \ + * sriov_max_vq_per_vf= \ * subsys= * -device nvme-ns,drive=,bus=,nsid=,\ * zoned=, \ @@ -71,7 +76,7 @@ * the SUBNQN field in the controller will report the NQN of the subsystem * device. This also enables multi controller capability represented in * Identify Controller data structure in CMIC (Controller Multi-path I/O and - * Namesapce Sharing Capabilities). + * Namespace Sharing Capabilities). * * - `aerl` * The Asynchronous Event Request Limit (AERL). Indicates the maximum number @@ -106,6 +111,35 @@ * transitioned to zone state closed for resource management purposes. * Defaults to 'on'. * + * - `sriov_max_vfs` + * Indicates the maximum number of PCIe virtual functions supported + * by the controller. The default value is 0. Specifying a non-zero value + * enables reporting of both SR-IOV and ARI capabilities by the NVMe device. + * Virtual function controllers will not report SR-IOV capability. + * + * NOTE: Single Root I/O Virtualization support is experimental. + * All the related parameters may be subject to change. + * + * - `sriov_vq_flexible` + * Indicates the total number of flexible queue resources assignable to all + * the secondary controllers. Implicitly sets the number of primary + * controller's private resources to `(max_ioqpairs - sriov_vq_flexible)`. + * + * - `sriov_vi_flexible` + * Indicates the total number of flexible interrupt resources assignable to + * all the secondary controllers. Implicitly sets the number of primary + * controller's private resources to `(msix_qsize - sriov_vi_flexible)`. + * + * - `sriov_max_vi_per_vf` + * Indicates the maximum number of virtual interrupt resources assignable + * to a secondary controller. The default 0 resolves to + * `(sriov_vi_flexible / sriov_max_vfs)`. + * + * - `sriov_max_vq_per_vf` + * Indicates the maximum number of virtual queue resources assignable to + * a secondary controller. The default 0 resolves to + * `(sriov_vq_flexible / sriov_max_vfs)`. + * * nvme namespace device parameters * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - `shared` @@ -154,12 +188,14 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/units.h" +#include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "sysemu/hostmem.h" #include "hw/pci/msix.h" +#include "hw/pci/pcie_sriov.h" #include "migration/vmstate.h" #include "nvme.h" @@ -176,6 +212,10 @@ #define NVME_TEMPERATURE_CRITICAL 0x175 #define NVME_NUM_FW_SLOTS 1 #define NVME_DEFAULT_MAX_ZA_SIZE (128 * KiB) +#define NVME_MAX_VFS 127 +#define NVME_VF_RES_GRANULARITY 1 +#define NVME_VF_OFFSET 0x1 +#define NVME_VF_STRIDE 1 #define NVME_GUEST_ERR(trace, fmt, ...) \ do { \ @@ -223,6 +263,8 @@ static const uint32_t nvme_cse_acs[256] = { [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, + [NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP, + [NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, }; @@ -254,6 +296,7 @@ static const uint32_t nvme_cse_iocs_zoned[256] = { }; static void nvme_process_sq(void *opaque); +static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst); static uint16_t nvme_sqid(NvmeRequest *req) { @@ -437,12 +480,12 @@ static bool nvme_nsid_valid(NvmeCtrl *n, uint32_t nsid) static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid) { - return sqid < n->params.max_ioqpairs + 1 && n->sq[sqid] != NULL ? 0 : -1; + return sqid < n->conf_ioqpairs + 1 && n->sq[sqid] != NULL ? 0 : -1; } static int nvme_check_cqid(NvmeCtrl *n, uint16_t cqid) { - return cqid < n->params.max_ioqpairs + 1 && n->cq[cqid] != NULL ? 0 : -1; + return cqid < n->conf_ioqpairs + 1 && n->cq[cqid] != NULL ? 0 : -1; } static void nvme_inc_cq_tail(NvmeCQueue *cq) @@ -808,10 +851,6 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, uint8_t type = NVME_SGL_TYPE(segment[i].type); switch (type) { - case NVME_SGL_DESCR_TYPE_BIT_BUCKET: - if (cmd->opcode == NVME_CMD_WRITE) { - continue; - } case NVME_SGL_DESCR_TYPE_DATA_BLOCK: break; case NVME_SGL_DESCR_TYPE_SEGMENT: @@ -844,10 +883,6 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, trans_len = MIN(*len, dlen); - if (type == NVME_SGL_DESCR_TYPE_BIT_BUCKET) { - goto next; - } - addr = le64_to_cpu(segment[i].addr); if (UINT64_MAX - addr < dlen) { @@ -859,7 +894,6 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, return status; } -next: *len -= trans_len; } @@ -917,8 +951,7 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, seg_len = le32_to_cpu(sgld->len); /* check the length of the (Last) Segment descriptor */ - if ((!seg_len || seg_len & 0xf) && - (NVME_SGL_TYPE(sgld->type) != NVME_SGL_DESCR_TYPE_BIT_BUCKET)) { + if (!seg_len || seg_len & 0xf) { return NVME_INVALID_SGL_SEG_DESCR | NVME_DNR; } @@ -956,26 +989,20 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, last_sgld = &segment[nsgld - 1]; /* - * If the segment ends with a Data Block or Bit Bucket Descriptor Type, - * then we are done. + * If the segment ends with a Data Block, then we are done. */ - switch (NVME_SGL_TYPE(last_sgld->type)) { - case NVME_SGL_DESCR_TYPE_DATA_BLOCK: - case NVME_SGL_DESCR_TYPE_BIT_BUCKET: + if (NVME_SGL_TYPE(last_sgld->type) == NVME_SGL_DESCR_TYPE_DATA_BLOCK) { status = nvme_map_sgl_data(n, sg, segment, nsgld, &len, cmd); if (status) { goto unmap; } goto out; - - default: - break; } /* - * If the last descriptor was not a Data Block or Bit Bucket, then the - * current segment must not be a Last Segment. + * If the last descriptor was not a Data Block, then the current + * segment must not be a Last Segment. */ if (NVME_SGL_TYPE(sgld->type) == NVME_SGL_DESCR_TYPE_LAST_SEGMENT) { status = NVME_INVALID_SGL_SEG_DESCR | NVME_DNR; @@ -1304,6 +1331,13 @@ static inline void nvme_blk_write(BlockBackend *blk, int64_t offset, } } +static void nvme_update_cq_head(NvmeCQueue *cq) +{ + pci_dma_read(&cq->ctrl->parent_obj, cq->db_addr, &cq->head, + sizeof(cq->head)); + trace_pci_nvme_shadow_doorbell_cq(cq->cqid, cq->head); +} + static void nvme_post_cqes(void *opaque) { NvmeCQueue *cq = opaque; @@ -1316,6 +1350,10 @@ static void nvme_post_cqes(void *opaque) NvmeSQueue *sq; hwaddr addr; + if (n->dbbuf_enabled) { + nvme_update_cq_head(cq); + } + if (nvme_cq_full(cq)) { break; } @@ -1362,7 +1400,14 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) QTAILQ_REMOVE(&req->sq->out_req_list, req, entry); QTAILQ_INSERT_TAIL(&cq->req_list, req, entry); - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); + + if (req->sq->ioeventfd_enabled) { + /* Post CQE directly since we are in main loop thread */ + nvme_post_cqes(cq); + } else { + /* Schedule the timer to post CQE later since we are in vcpu thread */ + timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); + } } static void nvme_process_aers(void *opaque) @@ -4188,10 +4233,82 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_OPCODE | NVME_DNR; } +static void nvme_cq_notifier(EventNotifier *e) +{ + NvmeCQueue *cq = container_of(e, NvmeCQueue, notifier); + NvmeCtrl *n = cq->ctrl; + + event_notifier_test_and_clear(&cq->notifier); + + nvme_update_cq_head(cq); + + if (cq->tail == cq->head) { + if (cq->irq_enabled) { + n->cq_pending--; + } + + nvme_irq_deassert(n, cq); + } + + nvme_post_cqes(cq); +} + +static int nvme_init_cq_ioeventfd(NvmeCQueue *cq) +{ + NvmeCtrl *n = cq->ctrl; + uint16_t offset = (cq->cqid << 3) + (1 << 2); + int ret; + + ret = event_notifier_init(&cq->notifier, 0); + if (ret < 0) { + return ret; + } + + event_notifier_set_handler(&cq->notifier, nvme_cq_notifier); + memory_region_add_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &cq->notifier); + + return 0; +} + +static void nvme_sq_notifier(EventNotifier *e) +{ + NvmeSQueue *sq = container_of(e, NvmeSQueue, notifier); + + event_notifier_test_and_clear(&sq->notifier); + + nvme_process_sq(sq); +} + +static int nvme_init_sq_ioeventfd(NvmeSQueue *sq) +{ + NvmeCtrl *n = sq->ctrl; + uint16_t offset = sq->sqid << 3; + int ret; + + ret = event_notifier_init(&sq->notifier, 0); + if (ret < 0) { + return ret; + } + + event_notifier_set_handler(&sq->notifier, nvme_sq_notifier); + memory_region_add_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &sq->notifier); + + return 0; +} + static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) { + uint16_t offset = sq->sqid << 3; + n->sq[sq->sqid] = NULL; timer_free(sq->timer); + if (sq->ioeventfd_enabled) { + memory_region_del_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &sq->notifier); + event_notifier_cleanup(&sq->notifier); + } g_free(sq->io_req); if (sq->sqid) { g_free(sq); @@ -4261,6 +4378,17 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, } sq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_process_sq, sq); + if (n->dbbuf_enabled) { + sq->db_addr = n->dbbuf_dbs + (sqid << 3); + sq->ei_addr = n->dbbuf_eis + (sqid << 3); + + if (n->params.ioeventfd && sq->sqid != 0) { + if (!nvme_init_sq_ioeventfd(sq)) { + sq->ioeventfd_enabled = true; + } + } + } + assert(n->cq[cqid]); cq = n->cq[cqid]; QTAILQ_INSERT_TAIL(&(cq->sq_list), sq, entry); @@ -4284,8 +4412,7 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_err_invalid_create_sq_cqid(cqid); return NVME_INVALID_CQID | NVME_DNR; } - if (unlikely(!sqid || sqid > n->params.max_ioqpairs || - n->sq[sqid] != NULL)) { + if (unlikely(!sqid || sqid > n->conf_ioqpairs || n->sq[sqid] != NULL)) { trace_pci_nvme_err_invalid_create_sq_sqid(sqid); return NVME_INVALID_QID | NVME_DNR; } @@ -4563,8 +4690,15 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n) { + uint16_t offset = (cq->cqid << 3) + (1 << 2); + n->cq[cq->cqid] = NULL; timer_free(cq->timer); + if (cq->ioeventfd_enabled) { + memory_region_del_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &cq->notifier); + event_notifier_cleanup(&cq->notifier); + } if (msix_enabled(&n->parent_obj)) { msix_vector_unuse(&n->parent_obj, cq->vector); } @@ -4620,6 +4754,16 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, cq->head = cq->tail = 0; QTAILQ_INIT(&cq->req_list); QTAILQ_INIT(&cq->sq_list); + if (n->dbbuf_enabled) { + cq->db_addr = n->dbbuf_dbs + (cqid << 3) + (1 << 2); + cq->ei_addr = n->dbbuf_eis + (cqid << 3) + (1 << 2); + + if (n->params.ioeventfd && cqid != 0) { + if (!nvme_init_cq_ioeventfd(cq)) { + cq->ioeventfd_enabled = true; + } + } + } n->cq[cqid] = cq; cq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_post_cqes, cq); } @@ -4637,8 +4781,7 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_create_cq(prp1, cqid, vector, qsize, qflags, NVME_CQ_FLAGS_IEN(qflags) != 0); - if (unlikely(!cqid || cqid > n->params.max_ioqpairs || - n->cq[cqid] != NULL)) { + if (unlikely(!cqid || cqid > n->conf_ioqpairs || n->cq[cqid] != NULL)) { trace_pci_nvme_err_invalid_create_cq_cqid(cqid); return NVME_INVALID_QID | NVME_DNR; } @@ -4654,7 +4797,7 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_err_invalid_create_cq_vector(vector); return NVME_INVALID_IRQ_VECTOR | NVME_DNR; } - if (unlikely(vector >= n->params.msix_qsize)) { + if (unlikely(vector >= n->conf_msix_qsize)) { trace_pci_nvme_err_invalid_create_cq_vector(vector); return NVME_INVALID_IRQ_VECTOR | NVME_DNR; } @@ -4793,6 +4936,37 @@ static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req, return nvme_c2h(n, (uint8_t *)list, sizeof(list), req); } +static uint16_t nvme_identify_pri_ctrl_cap(NvmeCtrl *n, NvmeRequest *req) +{ + trace_pci_nvme_identify_pri_ctrl_cap(le16_to_cpu(n->pri_ctrl_cap.cntlid)); + + return nvme_c2h(n, (uint8_t *)&n->pri_ctrl_cap, + sizeof(NvmePriCtrlCap), req); +} + +static uint16_t nvme_identify_sec_ctrl_list(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeIdentify *c = (NvmeIdentify *)&req->cmd; + uint16_t pri_ctrl_id = le16_to_cpu(n->pri_ctrl_cap.cntlid); + uint16_t min_id = le16_to_cpu(c->ctrlid); + uint8_t num_sec_ctrl = n->sec_ctrl_list.numcntl; + NvmeSecCtrlList list = {0}; + uint8_t i; + + for (i = 0; i < num_sec_ctrl; i++) { + if (n->sec_ctrl_list.sec[i].scid >= min_id) { + list.numcntl = num_sec_ctrl - i; + memcpy(&list.sec, n->sec_ctrl_list.sec + i, + list.numcntl * sizeof(NvmeSecCtrlEntry)); + break; + } + } + + trace_pci_nvme_identify_sec_ctrl_list(pri_ctrl_id, list.numcntl); + + return nvme_c2h(n, (uint8_t *)&list, sizeof(list), req); +} + static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req, bool active) { @@ -5009,6 +5183,10 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) return nvme_identify_ctrl_list(n, req, true); case NVME_ID_CNS_CTRL_LIST: return nvme_identify_ctrl_list(n, req, false); + case NVME_ID_CNS_PRIMARY_CTRL_CAP: + return nvme_identify_pri_ctrl_cap(n, req); + case NVME_ID_CNS_SECONDARY_CTRL_LIST: + return nvme_identify_sec_ctrl_list(n, req); case NVME_ID_CNS_CS_NS: return nvme_identify_ns_csi(n, req, true); case NVME_ID_CNS_CS_NS_PRESENT: @@ -5217,13 +5395,12 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) break; case NVME_NUMBER_OF_QUEUES: - result = (n->params.max_ioqpairs - 1) | - ((n->params.max_ioqpairs - 1) << 16); + result = (n->conf_ioqpairs - 1) | ((n->conf_ioqpairs - 1) << 16); trace_pci_nvme_getfeat_numq(result); break; case NVME_INTERRUPT_VECTOR_CONF: iv = dw11 & 0xffff; - if (iv >= n->params.max_ioqpairs + 1) { + if (iv >= n->conf_ioqpairs + 1) { return NVME_INVALID_FIELD | NVME_DNR; } @@ -5379,10 +5556,10 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_setfeat_numq((dw11 & 0xffff) + 1, ((dw11 >> 16) & 0xffff) + 1, - n->params.max_ioqpairs, - n->params.max_ioqpairs); - req->cqe.result = cpu_to_le32((n->params.max_ioqpairs - 1) | - ((n->params.max_ioqpairs - 1) << 16)); + n->conf_ioqpairs, + n->conf_ioqpairs); + req->cqe.result = cpu_to_le32((n->conf_ioqpairs - 1) | + ((n->conf_ioqpairs - 1) << 16)); break; case NVME_ASYNCHRONOUS_EVENT_CONF: n->features.async_config = dw11; @@ -5769,6 +5946,225 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req) return status; } +static void nvme_get_virt_res_num(NvmeCtrl *n, uint8_t rt, int *num_total, + int *num_prim, int *num_sec) +{ + *num_total = le32_to_cpu(rt ? + n->pri_ctrl_cap.vifrt : n->pri_ctrl_cap.vqfrt); + *num_prim = le16_to_cpu(rt ? + n->pri_ctrl_cap.virfap : n->pri_ctrl_cap.vqrfap); + *num_sec = le16_to_cpu(rt ? n->pri_ctrl_cap.virfa : n->pri_ctrl_cap.vqrfa); +} + +static uint16_t nvme_assign_virt_res_to_prim(NvmeCtrl *n, NvmeRequest *req, + uint16_t cntlid, uint8_t rt, + int nr) +{ + int num_total, num_prim, num_sec; + + if (cntlid != n->cntlid) { + return NVME_INVALID_CTRL_ID | NVME_DNR; + } + + nvme_get_virt_res_num(n, rt, &num_total, &num_prim, &num_sec); + + if (nr > num_total) { + return NVME_INVALID_NUM_RESOURCES | NVME_DNR; + } + + if (nr > num_total - num_sec) { + return NVME_INVALID_RESOURCE_ID | NVME_DNR; + } + + if (rt) { + n->next_pri_ctrl_cap.virfap = cpu_to_le16(nr); + } else { + n->next_pri_ctrl_cap.vqrfap = cpu_to_le16(nr); + } + + req->cqe.result = cpu_to_le32(nr); + return req->status; +} + +static void nvme_update_virt_res(NvmeCtrl *n, NvmeSecCtrlEntry *sctrl, + uint8_t rt, int nr) +{ + int prev_nr, prev_total; + + if (rt) { + prev_nr = le16_to_cpu(sctrl->nvi); + prev_total = le32_to_cpu(n->pri_ctrl_cap.virfa); + sctrl->nvi = cpu_to_le16(nr); + n->pri_ctrl_cap.virfa = cpu_to_le32(prev_total + nr - prev_nr); + } else { + prev_nr = le16_to_cpu(sctrl->nvq); + prev_total = le32_to_cpu(n->pri_ctrl_cap.vqrfa); + sctrl->nvq = cpu_to_le16(nr); + n->pri_ctrl_cap.vqrfa = cpu_to_le32(prev_total + nr - prev_nr); + } +} + +static uint16_t nvme_assign_virt_res_to_sec(NvmeCtrl *n, NvmeRequest *req, + uint16_t cntlid, uint8_t rt, int nr) +{ + int num_total, num_prim, num_sec, num_free, diff, limit; + NvmeSecCtrlEntry *sctrl; + + sctrl = nvme_sctrl_for_cntlid(n, cntlid); + if (!sctrl) { + return NVME_INVALID_CTRL_ID | NVME_DNR; + } + + if (sctrl->scs) { + return NVME_INVALID_SEC_CTRL_STATE | NVME_DNR; + } + + limit = le16_to_cpu(rt ? n->pri_ctrl_cap.vifrsm : n->pri_ctrl_cap.vqfrsm); + if (nr > limit) { + return NVME_INVALID_NUM_RESOURCES | NVME_DNR; + } + + nvme_get_virt_res_num(n, rt, &num_total, &num_prim, &num_sec); + num_free = num_total - num_prim - num_sec; + diff = nr - le16_to_cpu(rt ? sctrl->nvi : sctrl->nvq); + + if (diff > num_free) { + return NVME_INVALID_RESOURCE_ID | NVME_DNR; + } + + nvme_update_virt_res(n, sctrl, rt, nr); + req->cqe.result = cpu_to_le32(nr); + + return req->status; +} + +static uint16_t nvme_virt_set_state(NvmeCtrl *n, uint16_t cntlid, bool online) +{ + NvmeCtrl *sn = NULL; + NvmeSecCtrlEntry *sctrl; + int vf_index; + + sctrl = nvme_sctrl_for_cntlid(n, cntlid); + if (!sctrl) { + return NVME_INVALID_CTRL_ID | NVME_DNR; + } + + if (!pci_is_vf(&n->parent_obj)) { + vf_index = le16_to_cpu(sctrl->vfn) - 1; + sn = NVME(pcie_sriov_get_vf_at_index(&n->parent_obj, vf_index)); + } + + if (online) { + if (!sctrl->nvi || (le16_to_cpu(sctrl->nvq) < 2) || !sn) { + return NVME_INVALID_SEC_CTRL_STATE | NVME_DNR; + } + + if (!sctrl->scs) { + sctrl->scs = 0x1; + nvme_ctrl_reset(sn, NVME_RESET_FUNCTION); + } + } else { + nvme_update_virt_res(n, sctrl, NVME_VIRT_RES_INTERRUPT, 0); + nvme_update_virt_res(n, sctrl, NVME_VIRT_RES_QUEUE, 0); + + if (sctrl->scs) { + sctrl->scs = 0x0; + if (sn) { + nvme_ctrl_reset(sn, NVME_RESET_FUNCTION); + } + } + } + + return NVME_SUCCESS; +} + +static uint16_t nvme_virt_mngmt(NvmeCtrl *n, NvmeRequest *req) +{ + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10); + uint32_t dw11 = le32_to_cpu(req->cmd.cdw11); + uint8_t act = dw10 & 0xf; + uint8_t rt = (dw10 >> 8) & 0x7; + uint16_t cntlid = (dw10 >> 16) & 0xffff; + int nr = dw11 & 0xffff; + + trace_pci_nvme_virt_mngmt(nvme_cid(req), act, cntlid, rt ? "VI" : "VQ", nr); + + if (rt != NVME_VIRT_RES_QUEUE && rt != NVME_VIRT_RES_INTERRUPT) { + return NVME_INVALID_RESOURCE_ID | NVME_DNR; + } + + switch (act) { + case NVME_VIRT_MNGMT_ACTION_SEC_ASSIGN: + return nvme_assign_virt_res_to_sec(n, req, cntlid, rt, nr); + case NVME_VIRT_MNGMT_ACTION_PRM_ALLOC: + return nvme_assign_virt_res_to_prim(n, req, cntlid, rt, nr); + case NVME_VIRT_MNGMT_ACTION_SEC_ONLINE: + return nvme_virt_set_state(n, cntlid, true); + case NVME_VIRT_MNGMT_ACTION_SEC_OFFLINE: + return nvme_virt_set_state(n, cntlid, false); + default: + return NVME_INVALID_FIELD | NVME_DNR; + } +} + +static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req) +{ + uint64_t dbs_addr = le64_to_cpu(req->cmd.dptr.prp1); + uint64_t eis_addr = le64_to_cpu(req->cmd.dptr.prp2); + int i; + + /* Address should be page aligned */ + if (dbs_addr & (n->page_size - 1) || eis_addr & (n->page_size - 1)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + /* Save shadow buffer base addr for use during queue creation */ + n->dbbuf_dbs = dbs_addr; + n->dbbuf_eis = eis_addr; + n->dbbuf_enabled = true; + + for (i = 0; i < n->params.max_ioqpairs + 1; i++) { + NvmeSQueue *sq = n->sq[i]; + NvmeCQueue *cq = n->cq[i]; + + if (sq) { + /* + * CAP.DSTRD is 0, so offset of ith sq db_addr is (i<<3) + * nvme_process_db() uses this hard-coded way to calculate + * doorbell offsets. Be consistent with that here. + */ + sq->db_addr = dbs_addr + (i << 3); + sq->ei_addr = eis_addr + (i << 3); + pci_dma_write(&n->parent_obj, sq->db_addr, &sq->tail, + sizeof(sq->tail)); + + if (n->params.ioeventfd && sq->sqid != 0) { + if (!nvme_init_sq_ioeventfd(sq)) { + sq->ioeventfd_enabled = true; + } + } + } + + if (cq) { + /* CAP.DSTRD is 0, so offset of ith cq db_addr is (i<<3)+(1<<2) */ + cq->db_addr = dbs_addr + (i << 3) + (1 << 2); + cq->ei_addr = eis_addr + (i << 3) + (1 << 2); + pci_dma_write(&n->parent_obj, cq->db_addr, &cq->head, + sizeof(cq->head)); + + if (n->params.ioeventfd && cq->cqid != 0) { + if (!nvme_init_cq_ioeventfd(cq)) { + cq->ioeventfd_enabled = true; + } + } + } + } + + trace_pci_nvme_dbbuf_config(dbs_addr, eis_addr); + + return NVME_SUCCESS; +} + static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) { trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode, @@ -5811,6 +6207,10 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) return nvme_aer(n, req); case NVME_ADM_CMD_NS_ATTACHMENT: return nvme_ns_attachment(n, req); + case NVME_ADM_CMD_VIRT_MNGMT: + return nvme_virt_mngmt(n, req); + case NVME_ADM_CMD_DBBUF_CONFIG: + return nvme_dbbuf_config(n, req); case NVME_ADM_CMD_FORMAT_NVM: return nvme_format(n, req); default: @@ -5820,6 +6220,20 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_OPCODE | NVME_DNR; } +static void nvme_update_sq_eventidx(const NvmeSQueue *sq) +{ + pci_dma_write(&sq->ctrl->parent_obj, sq->ei_addr, &sq->tail, + sizeof(sq->tail)); + trace_pci_nvme_eventidx_sq(sq->sqid, sq->tail); +} + +static void nvme_update_sq_tail(NvmeSQueue *sq) +{ + pci_dma_read(&sq->ctrl->parent_obj, sq->db_addr, &sq->tail, + sizeof(sq->tail)); + trace_pci_nvme_shadow_doorbell_sq(sq->sqid, sq->tail); +} + static void nvme_process_sq(void *opaque) { NvmeSQueue *sq = opaque; @@ -5831,6 +6245,10 @@ static void nvme_process_sq(void *opaque) NvmeCmd cmd; NvmeRequest *req; + if (n->dbbuf_enabled) { + nvme_update_sq_tail(sq); + } + while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { addr = sq->dma_addr + sq->head * n->sqe_size; if (nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd))) { @@ -5854,11 +6272,56 @@ static void nvme_process_sq(void *opaque) req->status = status; nvme_enqueue_req_completion(cq, req); } + + if (n->dbbuf_enabled) { + nvme_update_sq_eventidx(sq); + nvme_update_sq_tail(sq); + } + } +} + +static void nvme_update_msixcap_ts(PCIDevice *pci_dev, uint32_t table_size) +{ + uint8_t *config; + + if (!msix_present(pci_dev)) { + return; + } + + assert(table_size > 0 && table_size <= pci_dev->msix_entries_nr); + + config = pci_dev->config + pci_dev->msix_cap; + pci_set_word_by_mask(config + PCI_MSIX_FLAGS, PCI_MSIX_FLAGS_QSIZE, + table_size - 1); +} + +static void nvme_activate_virt_res(NvmeCtrl *n) +{ + PCIDevice *pci_dev = &n->parent_obj; + NvmePriCtrlCap *cap = &n->pri_ctrl_cap; + NvmeSecCtrlEntry *sctrl; + + /* -1 to account for the admin queue */ + if (pci_is_vf(pci_dev)) { + sctrl = nvme_sctrl(n); + cap->vqprt = sctrl->nvq; + cap->viprt = sctrl->nvi; + n->conf_ioqpairs = sctrl->nvq ? le16_to_cpu(sctrl->nvq) - 1 : 0; + n->conf_msix_qsize = sctrl->nvi ? le16_to_cpu(sctrl->nvi) : 1; + } else { + cap->vqrfap = n->next_pri_ctrl_cap.vqrfap; + cap->virfap = n->next_pri_ctrl_cap.virfap; + n->conf_ioqpairs = le16_to_cpu(cap->vqprt) + + le16_to_cpu(cap->vqrfap) - 1; + n->conf_msix_qsize = le16_to_cpu(cap->viprt) + + le16_to_cpu(cap->virfap); } } -static void nvme_ctrl_reset(NvmeCtrl *n) +static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) { + PCIDevice *pci_dev = &n->parent_obj; + NvmeSecCtrlEntry *sctrl; NvmeNamespace *ns; int i; @@ -5888,9 +6351,45 @@ static void nvme_ctrl_reset(NvmeCtrl *n) g_free(event); } + if (n->params.sriov_max_vfs) { + if (!pci_is_vf(pci_dev)) { + for (i = 0; i < n->sec_ctrl_list.numcntl; i++) { + sctrl = &n->sec_ctrl_list.sec[i]; + nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); + } + + if (rst != NVME_RESET_CONTROLLER) { + pcie_sriov_pf_disable_vfs(pci_dev); + } + } + + if (rst != NVME_RESET_CONTROLLER) { + nvme_activate_virt_res(n); + } + } + n->aer_queued = 0; + n->aer_mask = 0; n->outstanding_aers = 0; n->qs_created = false; + + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); + + if (pci_is_vf(pci_dev)) { + sctrl = nvme_sctrl(n); + + stl_le_p(&n->bar.csts, sctrl->scs ? 0 : NVME_CSTS_FAILED); + } else { + stl_le_p(&n->bar.csts, 0); + } + + stl_le_p(&n->bar.intms, 0); + stl_le_p(&n->bar.intmc, 0); + stl_le_p(&n->bar.cc, 0); + + n->dbbuf_dbs = 0; + n->dbbuf_eis = 0; + n->dbbuf_enabled = false; } static void nvme_ctrl_shutdown(NvmeCtrl *n) @@ -5936,7 +6435,15 @@ static int nvme_start_ctrl(NvmeCtrl *n) uint64_t acq = ldq_le_p(&n->bar.acq); uint32_t page_bits = NVME_CC_MPS(cc) + 12; uint32_t page_size = 1 << page_bits; + NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); + if (pci_is_vf(&n->parent_obj) && !sctrl->scs) { + trace_pci_nvme_err_startfail_virt_state(le16_to_cpu(sctrl->nvi), + le16_to_cpu(sctrl->nvq), + sctrl->scs ? "ONLINE" : + "OFFLINE"); + return -1; + } if (unlikely(n->cq[0])) { trace_pci_nvme_err_startfail_cq(); return -1; @@ -6017,8 +6524,6 @@ static int nvme_start_ctrl(NvmeCtrl *n) nvme_set_timestamp(n, 0ULL); - QTAILQ_INIT(&n->aer_queue); - nvme_select_iocs(n); return 0; @@ -6096,20 +6601,21 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, nvme_irq_check(n); break; case NVME_REG_CC: + stl_le_p(&n->bar.cc, data); + trace_pci_nvme_mmio_cfg(data & 0xffffffff); - /* Windows first sends data, then sends enable bit */ - if (!NVME_CC_EN(data) && !NVME_CC_EN(cc) && - !NVME_CC_SHN(data) && !NVME_CC_SHN(cc)) - { - cc = data; + if (NVME_CC_SHN(data) && !(NVME_CC_SHN(cc))) { + trace_pci_nvme_mmio_shutdown_set(); + nvme_ctrl_shutdown(n); + csts &= ~(CSTS_SHST_MASK << CSTS_SHST_SHIFT); + csts |= NVME_CSTS_SHST_COMPLETE; + } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(cc)) { + trace_pci_nvme_mmio_shutdown_cleared(); + csts &= ~(CSTS_SHST_MASK << CSTS_SHST_SHIFT); } if (NVME_CC_EN(data) && !NVME_CC_EN(cc)) { - cc = data; - - /* flush CC since nvme_start_ctrl() needs the value */ - stl_le_p(&n->bar.cc, cc); if (unlikely(nvme_start_ctrl(n))) { trace_pci_nvme_err_startfail(); csts = NVME_CSTS_FAILED; @@ -6119,23 +6625,11 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, } } else if (!NVME_CC_EN(data) && NVME_CC_EN(cc)) { trace_pci_nvme_mmio_stopped(); - nvme_ctrl_reset(n); - cc = 0; - csts &= ~NVME_CSTS_READY; - } + nvme_ctrl_reset(n, NVME_RESET_CONTROLLER); - if (NVME_CC_SHN(data) && !(NVME_CC_SHN(cc))) { - trace_pci_nvme_mmio_shutdown_set(); - nvme_ctrl_shutdown(n); - cc = data; - csts |= NVME_CSTS_SHST_COMPLETE; - } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(cc)) { - trace_pci_nvme_mmio_shutdown_cleared(); - csts &= ~NVME_CSTS_SHST_COMPLETE; - cc = data; + break; } - stl_le_p(&n->bar.cc, cc); stl_le_p(&n->bar.csts, csts); break; @@ -6319,6 +6813,12 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) return 0; } + if (pci_is_vf(&n->parent_obj) && !nvme_sctrl(n)->scs && + addr != NVME_REG_CSTS) { + trace_pci_nvme_err_ignored_mmio_vf_offline(addr, size); + return 0; + } + /* * When PMRWBM bit 1 is set then read from * from PMRSTS should ensure prior writes @@ -6400,6 +6900,10 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) start_sqs = nvme_cq_full(cq) ? 1 : 0; cq->head = new_head; + if (!qid && n->dbbuf_enabled) { + pci_dma_write(&n->parent_obj, cq->db_addr, &cq->head, + sizeof(cq->head)); + } if (start_sqs) { NvmeSQueue *sq; QTAILQ_FOREACH(sq, &cq->sq_list, entry) { @@ -6457,6 +6961,23 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) trace_pci_nvme_mmio_doorbell_sq(sq->sqid, new_tail); sq->tail = new_tail; + if (!qid && n->dbbuf_enabled) { + /* + * The spec states "the host shall also update the controller's + * corresponding doorbell property to match the value of that entry + * in the Shadow Doorbell buffer." + * + * Since this context is currently a VM trap, we can safely enforce + * the requirement from the device side in case the host is + * misbehaving. + * + * Note, we shouldn't have to do this, but various drivers + * including ones that run on Linux, are not updating Admin Queues, + * so we can't trust reading it for an appropriate sq tail. + */ + pci_dma_write(&n->parent_obj, sq->db_addr, &sq->tail, + sizeof(sq->tail)); + } timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); } } @@ -6468,6 +6989,12 @@ static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data, trace_pci_nvme_mmio_write(addr, data, size); + if (pci_is_vf(&n->parent_obj) && !nvme_sctrl(n)->scs && + addr != NVME_REG_CSTS) { + trace_pci_nvme_err_ignored_mmio_vf_offline(addr, size); + return; + } + if (addr < sizeof(n->bar)) { nvme_write_bar(n, addr, data, size); } else { @@ -6569,19 +7096,140 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) error_setg(errp, "vsl must be non-zero"); return; } + + if (params->sriov_max_vfs) { + if (!n->subsys) { + error_setg(errp, "subsystem is required for the use of SR-IOV"); + return; + } + + if (params->sriov_max_vfs > NVME_MAX_VFS) { + error_setg(errp, "sriov_max_vfs must be between 0 and %d", + NVME_MAX_VFS); + return; + } + + if (params->cmb_size_mb) { + error_setg(errp, "CMB is not supported with SR-IOV"); + return; + } + + if (n->pmr.dev) { + error_setg(errp, "PMR is not supported with SR-IOV"); + return; + } + + if (!params->sriov_vq_flexible || !params->sriov_vi_flexible) { + error_setg(errp, "both sriov_vq_flexible and sriov_vi_flexible" + " must be set for the use of SR-IOV"); + return; + } + + if (params->sriov_vq_flexible < params->sriov_max_vfs * 2) { + error_setg(errp, "sriov_vq_flexible must be greater than or equal" + " to %d (sriov_max_vfs * 2)", params->sriov_max_vfs * 2); + return; + } + + if (params->max_ioqpairs < params->sriov_vq_flexible + 2) { + error_setg(errp, "(max_ioqpairs - sriov_vq_flexible) must be" + " greater than or equal to 2"); + return; + } + + if (params->sriov_vi_flexible < params->sriov_max_vfs) { + error_setg(errp, "sriov_vi_flexible must be greater than or equal" + " to %d (sriov_max_vfs)", params->sriov_max_vfs); + return; + } + + if (params->msix_qsize < params->sriov_vi_flexible + 1) { + error_setg(errp, "(msix_qsize - sriov_vi_flexible) must be" + " greater than or equal to 1"); + return; + } + + if (params->sriov_max_vi_per_vf && + (params->sriov_max_vi_per_vf - 1) % NVME_VF_RES_GRANULARITY) { + error_setg(errp, "sriov_max_vi_per_vf must meet:" + " (sriov_max_vi_per_vf - 1) %% %d == 0 and" + " sriov_max_vi_per_vf >= 1", NVME_VF_RES_GRANULARITY); + return; + } + + if (params->sriov_max_vq_per_vf && + (params->sriov_max_vq_per_vf < 2 || + (params->sriov_max_vq_per_vf - 1) % NVME_VF_RES_GRANULARITY)) { + error_setg(errp, "sriov_max_vq_per_vf must meet:" + " (sriov_max_vq_per_vf - 1) %% %d == 0 and" + " sriov_max_vq_per_vf >= 2", NVME_VF_RES_GRANULARITY); + return; + } + } } static void nvme_init_state(NvmeCtrl *n) { - /* add one to max_ioqpairs to account for the admin queue pair */ - n->reg_size = pow2ceil(sizeof(NvmeBar) + - 2 * (n->params.max_ioqpairs + 1) * NVME_DB_SIZE); + NvmePriCtrlCap *cap = &n->pri_ctrl_cap; + NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *sctrl; + uint8_t max_vfs; + int i; + + if (pci_is_vf(&n->parent_obj)) { + sctrl = nvme_sctrl(n); + max_vfs = 0; + n->conf_ioqpairs = sctrl->nvq ? le16_to_cpu(sctrl->nvq) - 1 : 0; + n->conf_msix_qsize = sctrl->nvi ? le16_to_cpu(sctrl->nvi) : 1; + } else { + max_vfs = n->params.sriov_max_vfs; + n->conf_ioqpairs = n->params.max_ioqpairs; + n->conf_msix_qsize = n->params.msix_qsize; + } + n->sq = g_new0(NvmeSQueue *, n->params.max_ioqpairs + 1); n->cq = g_new0(NvmeCQueue *, n->params.max_ioqpairs + 1); n->temperature = NVME_TEMPERATURE; n->features.temp_thresh_hi = NVME_TEMPERATURE_WARNING; n->starttime_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); + QTAILQ_INIT(&n->aer_queue); + + list->numcntl = cpu_to_le16(max_vfs); + for (i = 0; i < max_vfs; i++) { + sctrl = &list->sec[i]; + sctrl->pcid = cpu_to_le16(n->cntlid); + sctrl->vfn = cpu_to_le16(i + 1); + } + + cap->cntlid = cpu_to_le16(n->cntlid); + cap->crt = NVME_CRT_VQ | NVME_CRT_VI; + + if (pci_is_vf(&n->parent_obj)) { + cap->vqprt = cpu_to_le16(1 + n->conf_ioqpairs); + } else { + cap->vqprt = cpu_to_le16(1 + n->params.max_ioqpairs - + n->params.sriov_vq_flexible); + cap->vqfrt = cpu_to_le32(n->params.sriov_vq_flexible); + cap->vqrfap = cap->vqfrt; + cap->vqgran = cpu_to_le16(NVME_VF_RES_GRANULARITY); + cap->vqfrsm = n->params.sriov_max_vq_per_vf ? + cpu_to_le16(n->params.sriov_max_vq_per_vf) : + cap->vqfrt / MAX(max_vfs, 1); + } + + if (pci_is_vf(&n->parent_obj)) { + cap->viprt = cpu_to_le16(n->conf_msix_qsize); + } else { + cap->viprt = cpu_to_le16(n->params.msix_qsize - + n->params.sriov_vi_flexible); + cap->vifrt = cpu_to_le32(n->params.sriov_vi_flexible); + cap->virfap = cap->vifrt; + cap->vigran = cpu_to_le16(NVME_VF_RES_GRANULARITY); + cap->vifrsm = n->params.sriov_max_vi_per_vf ? + cpu_to_le16(n->params.sriov_max_vi_per_vf) : + cap->vifrt / MAX(max_vfs, 1); + } } static void nvme_init_cmb(NvmeCtrl *n, PCIDevice *pci_dev) @@ -6626,10 +7274,77 @@ 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) +{ + uint64_t bar_size, msix_table_size, msix_pba_size; + + bar_size = sizeof(NvmeBar) + 2 * total_queues * NVME_DB_SIZE; + bar_size = QEMU_ALIGN_UP(bar_size, 4 * KiB); + + if (msix_table_offset) { + *msix_table_offset = bar_size; + } + + msix_table_size = PCI_MSIX_ENTRY_SIZE * total_irqs; + bar_size += msix_table_size; + bar_size = QEMU_ALIGN_UP(bar_size, 4 * KiB); + + if (msix_pba_offset) { + *msix_pba_offset = bar_size; + } + + msix_pba_size = QEMU_ALIGN_UP(total_irqs, 64) / 8; + bar_size += msix_pba_size; + + bar_size = pow2ceil(bar_size); + return bar_size; +} + +static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) +{ + uint16_t vf_dev_id = n->params.use_intel_id ? + PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME; + NvmePriCtrlCap *cap = &n->pri_ctrl_cap; + uint64_t bar_size = nvme_bar_size(le16_to_cpu(cap->vqfrsm), + le16_to_cpu(cap->vifrsm), + NULL, NULL); + + pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE); + + pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); +} + +static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) +{ + Error *err = NULL; + int ret; + + ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, offset, + PCI_PM_SIZEOF, &err); + if (err) { + error_report_err(err); + return ret; + } + + pci_set_word(pci_dev->config + offset + PCI_PM_PMC, + PCI_PM_CAP_VER_1_2); + pci_set_word(pci_dev->config + offset + PCI_PM_CTRL, + PCI_PM_CTRL_NO_SOFT_RESET); + pci_set_word(pci_dev->wmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + + return 0; +} + static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) { uint8_t *pci_conf = pci_dev->config; - uint64_t bar_size, msix_table_size, msix_pba_size; + uint64_t bar_size; unsigned msix_table_offset, msix_pba_offset; int ret; @@ -6640,34 +7355,35 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) if (n->params.use_intel_id) { pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(pci_conf, 0x5845); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_NVME); } else { pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REDHAT_NVME); } pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_EXPRESS); + nvme_add_pm_capability(pci_dev, 0x60); pcie_endpoint_cap_init(pci_dev, 0x80); + pcie_cap_flr_init(pci_dev); + if (n->params.sriov_max_vfs) { + pcie_ari_init(pci_dev, 0x100, 1); + } - bar_size = QEMU_ALIGN_UP(n->reg_size, 4 * KiB); - msix_table_offset = bar_size; - msix_table_size = PCI_MSIX_ENTRY_SIZE * n->params.msix_qsize; - - bar_size += msix_table_size; - bar_size = QEMU_ALIGN_UP(bar_size, 4 * KiB); - msix_pba_offset = bar_size; - msix_pba_size = QEMU_ALIGN_UP(n->params.msix_qsize, 64) / 8; - - bar_size += msix_pba_size; - bar_size = pow2ceil(bar_size); + /* add one to max_ioqpairs to account for the admin queue pair */ + bar_size = nvme_bar_size(n->params.max_ioqpairs + 1, n->params.msix_qsize, + &msix_table_offset, &msix_pba_offset); memory_region_init(&n->bar0, OBJECT(n), "nvme-bar0", bar_size); memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", - n->reg_size); + msix_table_offset); memory_region_add_subregion(&n->bar0, 0, &n->iomem); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); + if (pci_is_vf(pci_dev)) { + pcie_sriov_vf_register_bar(pci_dev, 0, &n->bar0); + } else { + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); + } ret = msix_init(pci_dev, n->params.msix_qsize, &n->bar0, 0, msix_table_offset, &n->bar0, 0, msix_pba_offset, 0, &err); @@ -6680,6 +7396,8 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) } } + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); + if (n->params.cmb_size_mb) { nvme_init_cmb(n, pci_dev); } @@ -6688,6 +7406,10 @@ static int 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 0; } @@ -6709,6 +7431,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) NvmeIdCtrl *id = &n->id_ctrl; uint8_t *pci_conf = pci_dev->config; uint64_t cap = ldq_le_p(&n->bar.cap); + NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); 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)); @@ -6735,7 +7458,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); - id->oacs = cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT); + id->oacs = + cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT | NVME_OACS_DBBUF); id->cntrltype = 0x1; /* @@ -6775,8 +7499,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->vwc = NVME_VWC_NSID_BROADCAST_SUPPORT | NVME_VWC_PRESENT; id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1); - id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN | - NVME_CTRL_SGLS_BITBUCKET); + id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN); nvme_init_subnqn(n); @@ -6801,6 +7524,10 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) stl_le_p(&n->bar.vs, NVME_SPEC_VER); n->bar.intmc = n->bar.intms = 0; + + if (pci_is_vf(&n->parent_obj) && !sctrl->scs) { + stl_le_p(&n->bar.csts, NVME_CSTS_FAILED); + } } static int nvme_init_subsys(NvmeCtrl *n, Error **errp) @@ -6838,6 +7565,16 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) NvmeCtrl *n = NVME(pci_dev); NvmeNamespace *ns; Error *local_err = NULL; + NvmeCtrl *pn = NVME(pcie_sriov_get_pf(pci_dev)); + + if (pci_is_vf(pci_dev)) { + /* + * VFs derive settings from the parent. PF's lifespan exceeds + * that of VF's, so it's safe to share params.serial. + */ + memcpy(&n->params, &pn->params, sizeof(NvmeParams)); + n->subsys = pn->subsys; + } nvme_check_constraints(n, &local_err); if (local_err) { @@ -6848,15 +7585,14 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, &pci_dev->qdev, n->parent_obj.qdev.id); - nvme_init_state(n); - if (nvme_init_pci(n, pci_dev, errp)) { - return; - } - if (nvme_init_subsys(n, errp)) { error_propagate(errp, local_err); return; } + nvme_init_state(n); + if (nvme_init_pci(n, pci_dev, errp)) { + return; + } nvme_init_ctrl(n, pci_dev); /* setup a namespace if the controller drive property was given */ @@ -6878,7 +7614,7 @@ static void nvme_exit(PCIDevice *pci_dev) NvmeNamespace *ns; int i; - nvme_ctrl_reset(n); + nvme_ctrl_reset(n, NVME_RESET_FUNCTION); if (n->subsys) { for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { @@ -6902,6 +7638,11 @@ static void nvme_exit(PCIDevice *pci_dev) if (n->pmr.dev) { host_memory_backend_set_mapped(n->pmr.dev, false); } + + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { + pcie_sriov_pf_exit(pci_dev); + } + msix_uninit(pci_dev, &n->bar0, &n->bar0); memory_region_del_subregion(&n->bar0, &n->iomem); } @@ -6923,9 +7664,19 @@ static Property nvme_props[] = { DEFINE_PROP_UINT8("vsl", NvmeCtrl, params.vsl, 7), DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false), DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false), + DEFINE_PROP_BOOL("ioeventfd", NvmeCtrl, params.ioeventfd, 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_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(), }; @@ -6971,6 +7722,47 @@ static void nvme_set_smart_warning(Object *obj, Visitor *v, const char *name, } } +static void nvme_pci_reset(DeviceState *qdev) +{ + PCIDevice *pci_dev = PCI_DEVICE(qdev); + NvmeCtrl *n = NVME(pci_dev); + + trace_pci_nvme_pci_reset(); + nvme_ctrl_reset(n, NVME_RESET_FUNCTION); +} + +static void nvme_sriov_pre_write_ctrl(PCIDevice *dev, uint32_t address, + uint32_t val, int len) +{ + NvmeCtrl *n = NVME(dev); + NvmeSecCtrlEntry *sctrl; + uint16_t sriov_cap = dev->exp.sriov_cap; + uint32_t off = address - sriov_cap; + int i, num_vfs; + + 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); + } + } + } +} + +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); + pci_default_write_config(dev, address, val, len); + pcie_cap_flr_write_config(dev, address, val, len); +} + static const VMStateDescription nvme_vmstate = { .name = "nvme", .unmigratable = 1, @@ -6982,6 +7774,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); pc->realize = nvme_realize; + pc->config_write = nvme_pci_write_config; pc->exit = nvme_exit; pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->revision = 2; @@ -6990,6 +7783,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) dc->desc = "Non-Volatile Memory Express"; device_class_set_props(dc, nvme_props); dc->vmsd = &nvme_vmstate; + dc->reset = nvme_pci_reset; } static void nvme_instance_init(Object *obj) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 1b9c9d11567..62a1f97be01 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -546,6 +546,8 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) int i; if (!n->subsys) { + /* If no subsys, the ns cannot be attached to more than one ctrl. */ + ns->params.shared = false; if (ns->params.detached) { error_setg(errp, "detached requires that the nvme device is " "linked to an nvme-subsys device"); @@ -597,7 +599,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { NvmeCtrl *ctrl = subsys->ctrls[i]; - if (ctrl) { + if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { nvme_attach_ns(ctrl, ns); } } diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index e41771604f5..79f5c281c22 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -24,7 +24,7 @@ #include "block/nvme.h" -#define NVME_MAX_CONTROLLERS 32 +#define NVME_MAX_CONTROLLERS 256 #define NVME_MAX_NAMESPACES 256 #define NVME_EUI64_DEFAULT ((uint64_t)0x5254000000000000) @@ -43,6 +43,7 @@ typedef struct NvmeBus { #define TYPE_NVME_SUBSYS "nvme-subsys" #define NVME_SUBSYS(obj) \ OBJECT_CHECK(NvmeSubsystem, (obj), TYPE_NVME_SUBSYS) +#define SUBSYS_SLOT_RSVD (void *)0xFFFF typedef struct NvmeSubsystem { DeviceState parent_obj; @@ -68,6 +69,10 @@ static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys, return NULL; } + if (subsys->ctrls[cntlid] == SUBSYS_SLOT_RSVD) { + return NULL; + } + return subsys->ctrls[cntlid]; } @@ -335,6 +340,8 @@ static inline const char *nvme_adm_opc_str(uint8_t opc) case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES"; case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ"; case NVME_ADM_CMD_NS_ATTACHMENT: return "NVME_ADM_CMD_NS_ATTACHMENT"; + case NVME_ADM_CMD_VIRT_MNGMT: return "NVME_ADM_CMD_VIRT_MNGMT"; + case NVME_ADM_CMD_DBBUF_CONFIG: return "NVME_ADM_CMD_DBBUF_CONFIG"; case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM"; default: return "NVME_ADM_CMD_UNKNOWN"; } @@ -366,7 +373,11 @@ typedef struct NvmeSQueue { uint32_t tail; uint32_t size; uint64_t dma_addr; + uint64_t db_addr; + uint64_t ei_addr; QEMUTimer *timer; + EventNotifier notifier; + bool ioeventfd_enabled; NvmeRequest *io_req; QTAILQ_HEAD(, NvmeRequest) req_list; QTAILQ_HEAD(, NvmeRequest) out_req_list; @@ -383,7 +394,11 @@ typedef struct NvmeCQueue { uint32_t vector; uint32_t size; uint64_t dma_addr; + uint64_t db_addr; + uint64_t ei_addr; QEMUTimer *timer; + EventNotifier notifier; + bool ioeventfd_enabled; QTAILQ_HEAD(, NvmeSQueue) sq_list; QTAILQ_HEAD(, NvmeRequest) req_list; } NvmeCQueue; @@ -406,6 +421,12 @@ typedef struct NvmeParams { uint8_t zasl; bool auto_transition_zones; bool legacy_cmb; + bool ioeventfd; + uint8_t sriov_max_vfs; + uint16_t sriov_vq_flexible; + uint16_t sriov_vi_flexible; + uint8_t sriov_max_vq_per_vf; + uint8_t sriov_max_vi_per_vf; } NvmeParams; typedef struct NvmeCtrl { @@ -423,7 +444,6 @@ typedef struct NvmeCtrl { uint16_t max_prp_ents; uint16_t cqe_size; uint16_t sqe_size; - uint32_t reg_size; uint32_t max_q_ents; uint8_t outstanding_aers; uint32_t irq_status; @@ -433,6 +453,11 @@ typedef struct NvmeCtrl { uint64_t starttime_ms; uint16_t temperature; uint8_t smart_critical_warning; + uint32_t conf_msix_qsize; + uint32_t conf_ioqpairs; + uint64_t dbbuf_dbs; + uint64_t dbbuf_eis; + bool dbbuf_enabled; struct { MemoryRegion mem; @@ -477,8 +502,20 @@ typedef struct NvmeCtrl { uint32_t async_config; NvmeHostBehaviorSupport hbs; } features; + + NvmePriCtrlCap pri_ctrl_cap; + NvmeSecCtrlList sec_ctrl_list; + struct { + uint16_t vqrfap; + uint16_t virfap; + } next_pri_ctrl_cap; /* These override pri_ctrl_cap after reset */ } NvmeCtrl; +typedef enum NvmeResetType { + NVME_RESET_FUNCTION = 0, + NVME_RESET_CONTROLLER = 1, +} NvmeResetType; + static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid) { if (!nsid || nsid > NVME_MAX_NAMESPACES) { @@ -511,6 +548,33 @@ static inline uint16_t nvme_cid(NvmeRequest *req) return le16_to_cpu(req->cqe.cid); } +static inline NvmeSecCtrlEntry *nvme_sctrl(NvmeCtrl *n) +{ + PCIDevice *pci_dev = &n->parent_obj; + NvmeCtrl *pf = NVME(pcie_sriov_get_pf(pci_dev)); + + if (pci_is_vf(pci_dev)) { + return &pf->sec_ctrl_list.sec[pcie_sriov_vf_number(pci_dev)]; + } + + return NULL; +} + +static inline NvmeSecCtrlEntry *nvme_sctrl_for_cntlid(NvmeCtrl *n, + uint16_t cntlid) +{ + NvmeSecCtrlList *list = &n->sec_ctrl_list; + uint8_t i; + + for (i = 0; i < list->numcntl; i++) { + if (le16_to_cpu(list->sec[i].scid) == cntlid) { + return &list->sec[i]; + } + } + + return NULL; +} + void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns); uint16_t nvme_bounce_data(NvmeCtrl *n, void *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req); diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 691a90d2094..9d2643678b5 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -11,20 +11,71 @@ #include "nvme.h" -int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) +static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num) { NvmeSubsystem *subsys = n->subsys; - int cntlid, nsid; + NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *sctrl; + int i, cnt = 0; + + for (i = start; i < ARRAY_SIZE(subsys->ctrls) && cnt < num; i++) { + if (!subsys->ctrls[i]) { + sctrl = &list->sec[cnt]; + sctrl->scid = cpu_to_le16(i); + subsys->ctrls[i] = SUBSYS_SLOT_RSVD; + cnt++; + } + } + + return cnt; +} - for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { - if (!subsys->ctrls[cntlid]) { - break; +static void nvme_subsys_unreserve_cntlids(NvmeCtrl *n) +{ + NvmeSubsystem *subsys = n->subsys; + NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *sctrl; + int i, cntlid; + + for (i = 0; i < n->params.sriov_max_vfs; i++) { + sctrl = &list->sec[i]; + cntlid = le16_to_cpu(sctrl->scid); + + if (cntlid) { + assert(subsys->ctrls[cntlid] == SUBSYS_SLOT_RSVD); + subsys->ctrls[cntlid] = NULL; + sctrl->scid = 0; } } +} - if (cntlid == ARRAY_SIZE(subsys->ctrls)) { - error_setg(errp, "no more free controller id"); - return -1; +int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) +{ + NvmeSubsystem *subsys = n->subsys; + NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); + int cntlid, nsid, num_rsvd, num_vfs = n->params.sriov_max_vfs; + + if (pci_is_vf(&n->parent_obj)) { + cntlid = le16_to_cpu(sctrl->scid); + } else { + for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { + if (!subsys->ctrls[cntlid]) { + break; + } + } + + if (cntlid == ARRAY_SIZE(subsys->ctrls)) { + error_setg(errp, "no more free controller id"); + return -1; + } + + num_rsvd = nvme_subsys_reserve_cntlids(n, cntlid + 1, num_vfs); + if (num_rsvd != num_vfs) { + nvme_subsys_unreserve_cntlids(n); + error_setg(errp, + "no more free controller ids for secondary controllers"); + return -1; + } } if (!subsys->serial) { @@ -48,7 +99,13 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n) { - subsys->ctrls[n->cntlid] = NULL; + if (pci_is_vf(&n->parent_obj)) { + subsys->ctrls[n->cntlid] = SUBSYS_SLOT_RSVD; + } else { + subsys->ctrls[n->cntlid] = NULL; + nvme_subsys_unreserve_cntlids(n); + } + n->cntlid = -1; } diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events index ff1b4589692..fccb79f4897 100644 --- a/hw/nvme/trace-events +++ b/hw/nvme/trace-events @@ -3,6 +3,7 @@ pci_nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" pci_nvme_irq_pin(void) "pulsing IRQ pin" pci_nvme_irq_masked(void) "IRQ is masked" pci_nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64"" +pci_nvme_dbbuf_config(uint64_t dbs_addr, uint64_t eis_addr) "dbs_addr=0x%"PRIx64" eis_addr=0x%"PRIx64"" pci_nvme_map_addr(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64"" pci_nvme_map_addr_cmb(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64"" pci_nvme_map_prp(uint64_t trans_len, uint32_t len, uint64_t prp1, uint64_t prp2, int num_prps) "trans_len %"PRIu64" len %"PRIu32" prp1 0x%"PRIx64" prp2 0x%"PRIx64" num_prps %d" @@ -56,6 +57,8 @@ pci_nvme_identify_ctrl(void) "identify controller" pci_nvme_identify_ctrl_csi(uint8_t csi) "identify controller, csi=0x%"PRIx8"" pci_nvme_identify_ns(uint32_t ns) "nsid %"PRIu32"" pci_nvme_identify_ctrl_list(uint8_t cns, uint16_t cntid) "cns 0x%"PRIx8" cntid %"PRIu16"" +pci_nvme_identify_pri_ctrl_cap(uint16_t cntlid) "identify primary controller capabilities cntlid=%"PRIu16"" +pci_nvme_identify_sec_ctrl_list(uint16_t cntlid, uint8_t numcntl) "identify secondary controller list cntlid=%"PRIu16" numcntl=%"PRIu8"" pci_nvme_identify_ns_csi(uint32_t ns, uint8_t csi) "nsid=%"PRIu32", csi=0x%"PRIx8"" pci_nvme_identify_nslist(uint32_t ns) "nsid %"PRIu32"" pci_nvme_identify_nslist_csi(uint16_t ns, uint8_t csi) "nsid=%"PRIu16", csi=0x%"PRIx8"" @@ -81,6 +84,8 @@ pci_nvme_enqueue_event_noqueue(int queued) "queued %d" pci_nvme_enqueue_event_masked(uint8_t typ) "type 0x%"PRIx8"" pci_nvme_no_outstanding_aers(void) "ignoring event; no outstanding AERs" pci_nvme_enqueue_req_completion(uint16_t cid, uint16_t cqid, uint32_t dw0, uint32_t dw1, uint16_t status) "cid %"PRIu16" cqid %"PRIu16" dw0 0x%"PRIx32" dw1 0x%"PRIx32" status 0x%"PRIx16"" +pci_nvme_eventidx_cq(uint16_t cqid, uint16_t new_eventidx) "cqid %"PRIu16" new_eventidx %"PRIu16"" +pci_nvme_eventidx_sq(uint16_t sqid, uint16_t new_eventidx) "sqid %"PRIu16" new_eventidx %"PRIu16"" pci_nvme_mmio_read(uint64_t addr, unsigned size) "addr 0x%"PRIx64" size %d" pci_nvme_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" pci_nvme_mmio_doorbell_cq(uint16_t cqid, uint16_t new_head) "cqid %"PRIu16" new_head %"PRIu16"" @@ -97,6 +102,8 @@ pci_nvme_mmio_start_success(void) "setting controller enable bit succeeded" pci_nvme_mmio_stopped(void) "cleared controller enable bit" pci_nvme_mmio_shutdown_set(void) "shutdown bit set" pci_nvme_mmio_shutdown_cleared(void) "shutdown bit cleared" +pci_nvme_shadow_doorbell_cq(uint16_t cqid, uint16_t new_shadow_doorbell) "cqid %"PRIu16" new_shadow_doorbell %"PRIu16"" +pci_nvme_shadow_doorbell_sq(uint16_t sqid, uint16_t new_shadow_doorbell) "sqid %"PRIu16" new_shadow_doorbell %"PRIu16"" pci_nvme_open_zone(uint64_t slba, uint32_t zone_idx, int all) "open zone, slba=%"PRIu64", idx=%"PRIu32", all=%"PRIi32"" pci_nvme_close_zone(uint64_t slba, uint32_t zone_idx, int all) "close zone, slba=%"PRIu64", idx=%"PRIu32", all=%"PRIi32"" pci_nvme_finish_zone(uint64_t slba, uint32_t zone_idx, int all) "finish zone, slba=%"PRIu64", idx=%"PRIu32", all=%"PRIi32"" @@ -108,6 +115,8 @@ pci_nvme_zd_extension_set(uint32_t zone_idx) "set descriptor extension for zone_ pci_nvme_clear_ns_close(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Closed state" pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Empty state" pci_nvme_zoned_zrwa_implicit_flush(uint64_t zslba, uint32_t nlb) "zslba 0x%"PRIx64" nlb %"PRIu32"" +pci_nvme_pci_reset(void) "PCI Function Level Reset" +pci_nvme_virt_mngmt(uint16_t cid, uint16_t act, uint16_t cntlid, const char* rt, uint16_t nr) "cid %"PRIu16", act=0x%"PRIx16", ctrlid=%"PRIu16" %s nr=%"PRIu16"" # error conditions pci_nvme_err_mdts(size_t len) "len %zu" @@ -177,7 +186,9 @@ pci_nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the pci_nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero" pci_nvme_err_startfail_zasl_too_small(uint32_t zasl, uint32_t pagesz) "nvme_start_ctrl failed because zone append size limit %"PRIu32" is too small, needs to be >= %"PRIu32"" pci_nvme_err_startfail(void) "setting controller enable bit failed" +pci_nvme_err_startfail_virt_state(uint16_t vq, uint16_t vi, const char *state) "nvme_start_ctrl failed due to ctrl state: vi=%u vq=%u %s" pci_nvme_err_invalid_mgmt_action(uint8_t action) "action=0x%"PRIx8"" +pci_nvme_err_ignored_mmio_vf_offline(uint64_t addr, unsigned size) "addr 0x%"PRIx64" size %d" # undefined behavior pci_nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64"" diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 01a3093600f..2d4d8b952f3 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -64,8 +64,8 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) case I2C_START_RECV: DPRINTK("clear\n"); if (ee->blk && ee->changed) { - int len = blk_pwrite(ee->blk, 0, ee->mem, ee->rsize, 0); - if (len != ee->rsize) { + int ret = blk_pwrite(ee->blk, 0, ee->rsize, ee->mem, 0); + if (ret < 0) { ERR(TYPE_AT24C_EE " : failed to write backing file\n"); } @@ -75,6 +75,8 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) break; case I2C_NACK: break; + default: + return -1; } return 0; } @@ -163,9 +165,9 @@ void at24c_eeprom_reset(DeviceState *state) memset(ee->mem, 0, ee->rsize); if (ee->blk) { - int len = blk_pread(ee->blk, 0, ee->mem, ee->rsize); + int ret = blk_pread(ee->blk, 0, ee->rsize, ee->mem, 0); - if (len != ee->rsize) { + if (ret < 0) { ERR(TYPE_AT24C_EE " : Failed initial sync with backing file\n"); } diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 18b43be7f61..2d72f304422 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -103,7 +103,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, { SpaprNvram *nvram = spapr->nvram; hwaddr offset, buffer, len; - int alen; + int ret; void *membuf; if ((nargs != 3) || (nret != 2)) { @@ -128,9 +128,9 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, membuf = cpu_physical_memory_map(buffer, &len, false); - alen = len; + ret = 0; if (nvram->blk) { - alen = blk_pwrite(nvram->blk, offset, membuf, len, 0); + ret = blk_pwrite(nvram->blk, offset, len, membuf, 0); } assert(nvram->buf); @@ -138,8 +138,8 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, cpu_physical_memory_unmap(membuf, len, 0, len); - rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); - rtas_st(rets, 1, (alen < 0) ? 0 : alen); + rtas_st(rets, 0, (ret < 0) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); + rtas_st(rets, 1, (ret < 0) ? 0 : len); } static void spapr_nvram_realize(SpaprVioDevice *dev, Error **errp) @@ -179,9 +179,9 @@ static void spapr_nvram_realize(SpaprVioDevice *dev, Error **errp) } if (nvram->blk) { - int alen = blk_pread(nvram->blk, 0, nvram->buf, nvram->size); + ret = blk_pread(nvram->blk, 0, nvram->size, nvram->buf, 0); - if (alen != nvram->size) { + if (ret < 0) { error_setg(errp, "can't read spapr-nvram contents"); return; } @@ -224,7 +224,7 @@ static void postload_update_cb(void *opaque, bool running, RunState state) qemu_del_vm_change_state_handler(nvram->vmstate); nvram->vmstate = NULL; - blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size, 0); + blk_pwrite(nvram->blk, 0, nvram->size, nvram->buf, 0); } static int spapr_nvram_post_load(void *opaque, int version_id) diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 6ed32adad9f..c6b484cc85b 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -124,7 +124,7 @@ static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) blk_name(s->blk)); } - if (blk_pread(s->blk, 0, ram, nr) < 0) { + if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { error_setg(errp, "%s: Failed to read %u bytes from BBRAM backstore.", blk_name(s->blk), nr); @@ -159,7 +159,7 @@ static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) } offset = hwaddr - A_BBRAM_0; - rc = blk_pwrite(s->blk, offset, &le32, 4, 0); + rc = blk_pwrite(s->blk, offset, 4, &le32, 0); if (rc < 0) { bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset)); } diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index a0fd77b586d..fdfffaab99c 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -77,7 +77,7 @@ static int efuse_bdrv_read(XlnxEFuse *s, Error **errp) blk_name(s->blk)); } - if (blk_pread(s->blk, 0, ram, nr) < 0) { + if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { error_setg(errp, "%s: Failed to read %u bytes from eFUSE backstore.", blk_name(s->blk), nr); return -1; @@ -105,7 +105,7 @@ static void efuse_bdrv_sync(XlnxEFuse *s, unsigned int bit) le32 = cpu_to_le32(xlnx_efuse_get_row(s, bit)); row_offset = (bit / 32) * 4; - if (blk_pwrite(s->blk, row_offset, &le32, 4, 0) < 0) { + if (blk_pwrite(s->blk, row_offset, 4, &le32, 0) < 0) { error_report("%s: Failed to write offset %u of eFUSE backstore.", blk_name(s->blk), row_offset); } diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c new file mode 100644 index 00000000000..a361e519d04 --- /dev/null +++ b/hw/pci-bridge/cxl_downstream.c @@ -0,0 +1,249 @@ +/* + * Emulated CXL Switch Downstream Port + * + * Copyright (c) 2022 Huawei Technologies. + * + * Based on xio3130_downstream.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "qapi/error.h" + +typedef struct CXLDownStreamPort { + /*< private >*/ + PCIESlot parent_obj; + + /*< public >*/ + 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 +#define CXL_DOWNSTREAM_PORT_AER_OFFSET 0x100 +#define CXL_DOWNSTREAM_PORT_DVSEC_OFFSET \ + (CXL_DOWNSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF) + +static void latch_registers(CXLDownstreamPort *dsp) +{ + uint32_t *reg_state = dsp->cxl_cstate.crb.cache_mem_registers; + uint32_t *write_msk = dsp->cxl_cstate.crb.cache_mem_regs_write_mask; + + cxl_component_register_init_common(reg_state, write_msk, + CXL2_DOWNSTREAM_PORT); +} + +/* TODO: Look at sharing this code acorss all CXL port types */ +static void cxl_dsp_dvsec_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + CXLDownstreamPort *dsp = CXL_DSP(dev); + CXLComponentState *cxl_cstate = &dsp->cxl_cstate; + + if (range_contains(&cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC], addr)) { + uint8_t *reg = &dev->config[addr]; + addr -= cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC].lob; + if (addr == PORT_CONTROL_OFFSET) { + if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) { + /* unmask SBR */ + qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n"); + } + if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) { + /* Alt Memory & ID Space Enable */ + qemu_log_mask(LOG_UNIMP, + "Alt Memory & ID space is not supported\n"); + + } + } + } +} + +static void cxl_dsp_config_write(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + uint16_t slt_ctl, slt_sta; + + pcie_cap_slot_get(d, &slt_ctl, &slt_sta); + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len); + pcie_aer_write_config(d, address, val, len); + + cxl_dsp_dvsec_write_config(d, address, val, len); +} + +static void cxl_dsp_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + CXLDownstreamPort *dsp = CXL_DSP(qdev); + + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_cap_arifwd_reset(d); + pci_bridge_reset(qdev); + + latch_registers(dsp); +} + +static void build_dvsecs(CXLComponentState *cxl) +{ + uint8_t *dvsec; + + dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + EXTENSIONS_PORT_DVSEC_LENGTH, + EXTENSIONS_PORT_DVSEC, + EXTENSIONS_PORT_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){ + .cap = 0x27, /* Cache, IO, Mem, non-MLD */ + .ctrl = 0x02, /* IO always enabled */ + .status = 0x26, /* same */ + .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ + }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + PCIE_FLEXBUS_PORT_DVSEC_LENGTH_2_0, + PCIE_FLEXBUS_PORT_DVSEC, + PCIE_FLEXBUS_PORT_DVSEC_REVID_2_0, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECPortGPF){ + .rsvd = 0, + .phase1_ctrl = 1, /* 1μs timeout */ + .phase2_ctrl = 1, /* 1μs timeout */ + }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + GPF_PORT_DVSEC_LENGTH, GPF_PORT_DVSEC, + GPF_PORT_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){ + .rsvd = 0, + .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX, + .reg0_base_hi = 0, + }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC, + REG_LOC_DVSEC_REVID, dvsec); +} + +static void cxl_dsp_realize(PCIDevice *d, Error **errp) +{ + PCIEPort *p = PCIE_PORT(d); + PCIESlot *s = PCIE_SLOT(d); + CXLDownstreamPort *dsp = CXL_DSP(d); + CXLComponentState *cxl_cstate = &dsp->cxl_cstate; + ComponentRegisters *cregs = &cxl_cstate->crb; + MemoryRegion *component_bar = &cregs->component_registers; + int rc; + + pci_bridge_initfn(d, TYPE_PCIE_BUS); + pcie_port_init_reg(d); + + rc = msi_init(d, CXL_DOWNSTREAM_PORT_MSI_OFFSET, + CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR, + true, true, errp); + if (rc) { + assert(rc == -ENOTSUP); + goto err_bridge; + } + + rc = pcie_cap_init(d, CXL_DOWNSTREAM_PORT_EXP_OFFSET, + PCI_EXP_TYPE_DOWNSTREAM, p->port, + errp); + if (rc < 0) { + goto err_msi; + } + + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s); + pcie_cap_arifwd_init(d); + + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + error_setg(errp, "Can't add chassis slot, error %d", rc); + goto err_pcie_cap; + } + + rc = pcie_aer_init(d, PCI_ERR_VER, CXL_DOWNSTREAM_PORT_AER_OFFSET, + PCI_ERR_SIZEOF, errp); + if (rc < 0) { + goto err_chassis; + } + + cxl_cstate->dvsec_offset = CXL_DOWNSTREAM_PORT_DVSEC_OFFSET; + cxl_cstate->pdev = d; + build_dvsecs(cxl_cstate); + cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_DSP); + pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, + component_bar); + + return; + + err_chassis: + pcie_chassis_del_slot(s); + err_pcie_cap: + pcie_cap_exit(d); + err_msi: + msi_uninit(d); + err_bridge: + pci_bridge_exitfn(d); +} + +static void cxl_dsp_exitfn(PCIDevice *d) +{ + PCIESlot *s = PCIE_SLOT(d); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +static void cxl_dsp_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); + + k->is_bridge = true; + k->config_write = cxl_dsp_config_write; + k->realize = cxl_dsp_realize; + k->exit = cxl_dsp_exitfn; + k->vendor_id = 0x19e5; /* Huawei */ + k->device_id = 0xa129; /* Emulated CXL Switch Downstream Port */ + k->revision = 0; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "CXL Switch Downstream Port"; + dc->reset = cxl_dsp_reset; +} + +static const TypeInfo cxl_dsp_info = { + .name = TYPE_CXL_DSP, + .instance_size = sizeof(CXLDownstreamPort), + .parent = TYPE_PCIE_SLOT, + .class_init = cxl_dsp_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CXL_DEVICE }, + { } + }, +}; + +static void cxl_dsp_register_type(void) +{ + type_register_static(&cxl_dsp_info); +} + +type_init(cxl_dsp_register_type); diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c new file mode 100644 index 00000000000..a83a3e81e41 --- /dev/null +++ b/hw/pci-bridge/cxl_upstream.c @@ -0,0 +1,216 @@ +/* + * Emulated CXL Switch Upstream Port + * + * Copyright (c) 2022 Huawei Technologies. + * + * Based on xio3130_upstream.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" + +#define CXL_UPSTREAM_PORT_MSI_NR_VECTOR 1 + +#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 \ + (CXL_UPSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF) + +typedef struct CXLUpstreamPort { + /*< private >*/ + PCIEPort parent_obj; + + /*< public >*/ + CXLComponentState cxl_cstate; +} CXLUpstreamPort; + +CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp) +{ + return &usp->cxl_cstate; +} + +static void cxl_usp_dvsec_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + CXLUpstreamPort *usp = CXL_USP(dev); + + if (range_contains(&usp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC], addr)) { + uint8_t *reg = &dev->config[addr]; + addr -= usp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC].lob; + if (addr == PORT_CONTROL_OFFSET) { + if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) { + /* unmask SBR */ + qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n"); + } + if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) { + /* Alt Memory & ID Space Enable */ + qemu_log_mask(LOG_UNIMP, + "Alt Memory & ID space is not supported\n"); + } + } + } +} + +static void cxl_usp_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); + + cxl_usp_dvsec_write_config(d, address, val, len); +} + +static void latch_registers(CXLUpstreamPort *usp) +{ + uint32_t *reg_state = usp->cxl_cstate.crb.cache_mem_registers; + uint32_t *write_msk = usp->cxl_cstate.crb.cache_mem_regs_write_mask; + + cxl_component_register_init_common(reg_state, write_msk, + CXL2_UPSTREAM_PORT); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 8); +} + +static void cxl_usp_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + CXLUpstreamPort *usp = CXL_USP(qdev); + + pci_bridge_reset(qdev); + pcie_cap_deverr_reset(d); + latch_registers(usp); +} + +static void build_dvsecs(CXLComponentState *cxl) +{ + uint8_t *dvsec; + + dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ + .status = 0x1, /* Port Power Management Init Complete */ + }; + cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, + EXTENSIONS_PORT_DVSEC_LENGTH, + EXTENSIONS_PORT_DVSEC, + EXTENSIONS_PORT_DVSEC_REVID, dvsec); + dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){ + .cap = 0x27, /* Cache, IO, Mem, non-MLD */ + .ctrl = 0x27, /* Cache, IO, Mem */ + .status = 0x26, /* same */ + .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ + }; + cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, + PCIE_FLEXBUS_PORT_DVSEC_LENGTH_2_0, + PCIE_FLEXBUS_PORT_DVSEC, + PCIE_FLEXBUS_PORT_DVSEC_REVID_2_0, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){ + .rsvd = 0, + .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX, + .reg0_base_hi = 0, + }; + cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, + REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC, + REG_LOC_DVSEC_REVID, dvsec); +} + +static void cxl_usp_realize(PCIDevice *d, Error **errp) +{ + PCIEPort *p = PCIE_PORT(d); + CXLUpstreamPort *usp = CXL_USP(d); + CXLComponentState *cxl_cstate = &usp->cxl_cstate; + ComponentRegisters *cregs = &cxl_cstate->crb; + MemoryRegion *component_bar = &cregs->component_registers; + int rc; + + pci_bridge_initfn(d, TYPE_PCIE_BUS); + pcie_port_init_reg(d); + + rc = msi_init(d, CXL_UPSTREAM_PORT_MSI_OFFSET, + CXL_UPSTREAM_PORT_MSI_NR_VECTOR, true, true, errp); + if (rc) { + assert(rc == -ENOTSUP); + goto err_bridge; + } + + rc = pcie_cap_init(d, CXL_UPSTREAM_PORT_PCIE_CAP_OFFSET, + PCI_EXP_TYPE_UPSTREAM, p->port, errp); + if (rc < 0) { + goto err_msi; + } + + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + rc = pcie_aer_init(d, PCI_ERR_VER, CXL_UPSTREAM_PORT_AER_OFFSET, + PCI_ERR_SIZEOF, errp); + if (rc) { + goto err_cap; + } + + cxl_cstate->dvsec_offset = CXL_UPSTREAM_PORT_DVSEC_OFFSET; + cxl_cstate->pdev = d; + build_dvsecs(cxl_cstate); + cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_USP); + pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, + component_bar); + + return; + +err_cap: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + pci_bridge_exitfn(d); +} + +static void cxl_usp_exitfn(PCIDevice *d) +{ + pcie_aer_exit(d); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +static void cxl_upstream_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); + + k->is_bridge = true; + k->config_write = cxl_usp_write_config; + k->realize = cxl_usp_realize; + k->exit = cxl_usp_exitfn; + k->vendor_id = 0x19e5; /* Huawei */ + k->device_id = 0xa128; /* Emulated CXL Switch Upstream Port */ + k->revision = 0; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "CXL Switch Upstream Port"; + dc->reset = cxl_usp_reset; +} + +static const TypeInfo cxl_usp_info = { + .name = TYPE_CXL_USP, + .parent = TYPE_PCIE_PORT, + .instance_size = sizeof(CXLUpstreamPort), + .class_init = cxl_upstream_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CXL_DEVICE }, + { } + }, +}; + +static void cxl_usp_register_type(void) +{ + type_register_static(&cxl_usp_info); +} + +type_init(cxl_usp_register_type); diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build index fdbe2e07c50..243ceeda50f 100644 --- a/hw/pci-bridge/meson.build +++ b/hw/pci-bridge/meson.build @@ -6,7 +6,7 @@ pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pci pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'), if_false: files('pci_expander_bridge_stubs.c')) pci_ss.add(when: 'CONFIG_XIO3130', if_true: files('xio3130_upstream.c', 'xio3130_downstream.c')) -pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c')) +pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c', 'cxl_upstream.c', 'cxl_downstream.c')) # NewWorld PowerMac pci_ss.add(when: 'CONFIG_DEC_PCI', if_true: files('dec.c')) diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index e08716142b6..1c5ad5f918a 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -238,7 +238,6 @@ static void i440fx_realize(PCIDevice *dev, Error **errp) } PCIBus *i440fx_init(const char *host_type, const char *pci_type, - PCII440FXState **pi440fx_state, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, ram_addr_t ram_size, @@ -264,8 +263,7 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); d = pci_create_simple(b, 0, pci_type); - *pi440fx_state = I440FX_PCI_DEVICE(d); - f = *pi440fx_state; + f = I440FX_PCI_DEVICE(d); f->system_memory = address_space_mem; f->pci_address_space = pci_address_space; f->ram_memory = ram_memory; diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 3f03467dde0..d58d3c1701b 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -1052,7 +1052,8 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp) pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); - pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), TYPE_PNV_PHB3_ROOT_PORT); + pnv_phb_attach_root_port(pci, TYPE_PNV_PHB3_ROOT_PORT, + phb->phb_id, phb->chip_id); } void pnv_phb3_update_regions(PnvPHB3 *phb) @@ -1088,10 +1089,10 @@ static const char *pnv_phb3_root_bus_path(PCIHostState *host_bridge, } static Property pnv_phb3_properties[] = { - DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0), - DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), - DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), + DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_END_OF_LIST(), }; static void pnv_phb3_class_init(ObjectClass *klass, void *data) @@ -1129,33 +1130,14 @@ static const TypeInfo pnv_phb3_root_bus_info = { .name = TYPE_PNV_PHB3_ROOT_BUS, .parent = TYPE_PCIE_BUS, .class_init = pnv_phb3_root_bus_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_PCIE_DEVICE }, - { } - }, }; static void pnv_phb3_root_port_realize(DeviceState *dev, Error **errp) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); PCIDevice *pci = PCI_DEVICE(dev); - PCIBus *bus = pci_get_bus(pci); - PnvPHB3 *phb = NULL; Error *local_err = NULL; - phb = (PnvPHB3 *) object_dynamic_cast(OBJECT(bus->qbus.parent), - TYPE_PNV_PHB3); - - if (!phb) { - error_setg(errp, -"pnv_phb3_root_port devices must be connected to pnv-phb3 buses"); - return; - } - - /* Set unique chassis/slot values for the root port */ - qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id); - qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id); - rpc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 13ba9e45d8b..67ddde4a6e1 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -31,22 +31,6 @@ qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \ (pec)->chip_id, (pec)->index, ## __VA_ARGS__) -/* - * QEMU version of the GETFIELD/SETFIELD macros - * - * These are common with the PnvXive model. - */ -static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) -{ - return (word & mask) >> ctz64(mask); -} - -static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, - uint64_t value) -{ - return (word & ~mask) | ((value << ctz64(mask)) & mask); -} - static PCIDevice *pnv_phb4_find_cfg_dev(PnvPHB4 *phb) { PCIHostState *pci = PCI_HOST_BRIDGE(phb); @@ -1547,6 +1531,7 @@ static void pnv_phb4_instance_init(Object *obj) static void pnv_phb4_realize(DeviceState *dev, Error **errp) { PnvPHB4 *phb = PNV_PHB4(dev); + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(phb->pec); PCIHostState *pci = PCI_HOST_BRIDGE(dev); XiveSource *xsrc = &phb->xsrc; int nr_irqs; @@ -1583,6 +1568,10 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp) pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; + /* Add a single Root port if running with defaults */ + pnv_phb_attach_root_port(pci, pecc->rp_model, + phb->phb_id, phb->chip_id); + /* Setup XIVE Source */ if (phb->big_phb) { nr_irqs = PNV_PHB4_MAX_INTs; @@ -1692,11 +1681,11 @@ static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno, } static Property pnv_phb4_properties[] = { - DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), - DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), - DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, - PnvPhb4PecState *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), + DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, + PnvPhb4PecState *), + DEFINE_PROP_END_OF_LIST(), }; static void pnv_phb4_class_init(ObjectClass *klass, void *data) @@ -1747,10 +1736,6 @@ static const TypeInfo pnv_phb4_root_bus_info = { .name = TYPE_PNV_PHB4_ROOT_BUS, .parent = TYPE_PCIE_BUS, .class_init = pnv_phb4_root_bus_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_PCIE_DEVICE }, - { } - }, }; static void pnv_phb4_root_port_reset(DeviceState *dev) @@ -1777,23 +1762,8 @@ static void pnv_phb4_root_port_reset(DeviceState *dev) static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); - PCIDevice *pci = PCI_DEVICE(dev); - PCIBus *bus = pci_get_bus(pci); - PnvPHB4 *phb = NULL; Error *local_err = NULL; - phb = (PnvPHB4 *) object_dynamic_cast(OBJECT(bus->qbus.parent), - TYPE_PNV_PHB4); - - if (!phb) { - error_setg(errp, "%s must be connected to pnv-phb4 buses", dev->id); - return; - } - - /* Set unique chassis/slot values for the root port */ - qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id); - qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id); - rpc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index 61bc0b503e3..c9aaf1c28e3 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -130,9 +130,6 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) { return; } - - /* Add a single Root port if running with defaults */ - pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), pecc->rp_model); } static void pnv_pec_realize(DeviceState *dev, Error **errp) @@ -215,11 +212,11 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, } static Property pnv_pec_properties[] = { - DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), - DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), - DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, - PnvChip *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), + DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), + DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, + PnvChip *), + DEFINE_PROP_END_OF_LIST(), }; static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index ab5a47aff56..20da1213747 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -379,7 +379,8 @@ static void mch_update_smram(MCHPCIState *mch) memory_region_set_enabled(&mch->high_smram, false); } - if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) { + if ((pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) && + (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME)) { switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB: diff --git a/hw/pci/msi.c b/hw/pci/msi.c index 5c471b96165..058d1d1ef1b 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -322,9 +322,9 @@ void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp) bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; uint32_t irq_state, vector_mask, pending; - if (vector > PCI_MSI_VECTORS_MAX) { + if (vector >= PCI_MSI_VECTORS_MAX) { error_setg(errp, "msi: vector %d not allocated. max vector is %d", - vector, PCI_MSI_VECTORS_MAX); + vector, (PCI_MSI_VECTORS_MAX - 1)); return; } diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 92bd0530dd2..eff62f39455 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -323,7 +323,7 @@ static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) */ } - /* Errro Message Received: Root Error Status register */ + /* Error Message Received: Root Error Status register */ switch (msg->severity) { case PCI_ERR_ROOT_CMD_COR_EN: if (root_status & PCI_ERR_ROOT_COR_RCV) { diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index f822f18b980..e71f3a74837 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -480,7 +480,8 @@ static const MemoryRegionOps shpc_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { /* SHPC ECN requires dword accesses, but the original 1.0 spec doesn't. - * It's easier to suppport all sizes than worry about it. */ + * It's easier to support all sizes than worry about it. + */ .min_access_size = 1, .max_access_size = 4, }, diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 7f7f5b34526..32495d01235 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "qemu/guest-random.h" #include "qapi/error.h" #include "e500.h" #include "e500-ccsr.h" @@ -346,6 +347,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, }; const char *dtb_file = machine->dtb; const char *toplevel_compat = machine->dt_compatible; + uint8_t rng_seed[32]; if (dtb_file) { char *filename; @@ -403,6 +405,9 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + if (kvm_enabled()) { /* Read out host's frequencies */ clock_freq = kvmppc_get_clockfreq(); @@ -861,7 +866,6 @@ void ppce500_init(MachineState *machine) for (i = 0; i < smp_cpus; i++) { PowerPCCPU *cpu; CPUState *cs; - qemu_irq *input; cpu = POWERPC_CPU(object_new(machine->cpu_type)); env = &cpu->env; @@ -885,9 +889,10 @@ void ppce500_init(MachineState *machine) firstenv = env; } - input = (qemu_irq *)env->irq_inputs; - irqs[i].irq[OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; - irqs[i].irq[OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; + irqs[i].irq[OPENPIC_OUTPUT_INT] = + qdev_get_gpio_in(DEVICE(cpu), PPCE500_INPUT_INT); + irqs[i].irq[OPENPIC_OUTPUT_CINT] = + qdev_get_gpio_in(DEVICE(cpu), PPCE500_INPUT_CINT); env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index c865921bdc0..cf7eb723912 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -262,30 +262,30 @@ static void ppc_core99_init(MachineState *machine) switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_6xx: openpic_irqs[i].irq[OPENPIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_MCK] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_MCP]; + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_MCP); /* Not connected ? */ openpic_irqs[i].irq[OPENPIC_OUTPUT_DEBUG] = NULL; /* Check this */ openpic_irqs[i].irq[OPENPIC_OUTPUT_RESET] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_HRESET]; + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_HRESET); break; #if defined(TARGET_PPC64) case PPC_FLAGS_INPUT_970: openpic_irqs[i].irq[OPENPIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; + qdev_get_gpio_in(DEVICE(cpu), PPC970_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; + qdev_get_gpio_in(DEVICE(cpu), PPC970_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_MCK] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_MCP]; + qdev_get_gpio_in(DEVICE(cpu), PPC970_INPUT_MCP); /* Not connected ? */ openpic_irqs[i].irq[OPENPIC_OUTPUT_DEBUG] = NULL; /* Check this */ openpic_irqs[i].irq[OPENPIC_OUTPUT_RESET] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_HRESET]; + qdev_get_gpio_in(DEVICE(cpu), PPC970_INPUT_HRESET); break; #endif /* defined(TARGET_PPC64) */ default: diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index d62fdf0db3f..03732ca7ed0 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -271,7 +271,7 @@ static void ppc_heathrow_init(MachineState *machine) case PPC_FLAGS_INPUT_6xx: /* XXX: we register only 1 output pin for heathrow PIC */ qdev_connect_gpio_out(pic_dev, 0, - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); break; default: error_report("Bus model not supported on OldWorld Mac machine"); diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 9411ca6b16b..61f4263953b 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -155,7 +155,7 @@ static void pegasos2_init(MachineState *machine) /* Marvell Discovery II system controller */ pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT])); + qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT))); pci_bus = mv64361_get_pci_bus(pm->mv, 1); /* VIA VT8231 South Bridge (multifunction PCI device) */ diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7c08a78d6ce..d3f77c83672 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -652,25 +652,19 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } -static int pnv_chip_power8_pic_print_info_child(Object *child, void *opaque) -{ - Monitor *mon = opaque; - PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3); - - if (phb3) { - pnv_phb3_msi_pic_print_info(&phb3->msis, mon); - ics_pic_print_info(&phb3->lsis, mon); - } - return 0; -} - static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) { Pnv8Chip *chip8 = PNV8_CHIP(chip); + int i; ics_pic_print_info(&chip8->psi.ics, mon); - object_child_foreach(OBJECT(chip), - pnv_chip_power8_pic_print_info_child, mon); + + for (i = 0; i < chip8->num_phbs; i++) { + PnvPHB3 *phb3 = &chip8->phbs[i]; + + pnv_phb3_msi_pic_print_info(&phb3->msis, mon); + ics_pic_print_info(&phb3->lsis, mon); + } } static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) @@ -1189,10 +1183,26 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) } } -/* Attach a root port device */ -void pnv_phb_attach_root_port(PCIHostState *pci, const char *name) +/* + * Attach a root port device. + * + * 'index' will be used both as a PCIE slot value and to calculate + * QOM id. 'chip_id' is going to be used as PCIE chassis for the + * root port. + */ +void pnv_phb_attach_root_port(PCIHostState *pci, const char *name, + int index, int chip_id) { PCIDevice *root = pci_new(PCI_DEVFN(0, 0), name); + g_autofree char *default_id = g_strdup_printf("%s[%d]", name, index); + const char *dev_id = DEVICE(root)->id; + + object_property_add_child(OBJECT(pci->bus), dev_id ? dev_id : default_id, + OBJECT(root)); + + /* Set unique chassis/slot values for the root port */ + qdev_prop_set_uint8(DEVICE(root), "chassis", chip_id); + qdev_prop_set_uint16(DEVICE(root), "slot", index); pci_realize_and_unref(root, pci->bus, &error_fatal); } @@ -1934,44 +1944,28 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) return NULL; } -typedef struct ForeachPhb3Args { - int irq; - ICSState *ics; -} ForeachPhb3Args; - -static int pnv_ics_get_child(Object *child, void *opaque) -{ - ForeachPhb3Args *args = opaque; - PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3); - - if (phb3) { - if (ics_valid_irq(&phb3->lsis, args->irq)) { - args->ics = &phb3->lsis; - } - if (ics_valid_irq(ICS(&phb3->msis), args->irq)) { - args->ics = ICS(&phb3->msis); - } - } - return args->ics ? 1 : 0; -} - static ICSState *pnv_ics_get(XICSFabric *xi, int irq) { PnvMachineState *pnv = PNV_MACHINE(xi); - ForeachPhb3Args args = { irq, NULL }; - int i; + int i, j; for (i = 0; i < pnv->num_chips; i++) { - PnvChip *chip = pnv->chips[i]; Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); if (ics_valid_irq(&chip8->psi.ics, irq)) { return &chip8->psi.ics; } - object_child_foreach(OBJECT(chip), pnv_ics_get_child, &args); - if (args.ics) { - return args.ics; + for (j = 0; j < chip8->num_phbs; j++) { + PnvPHB3 *phb3 = &chip8->phbs[j]; + + if (ics_valid_irq(&phb3->lsis, irq)) { + return &phb3->lsis; + } + + if (ics_valid_irq(ICS(&phb3->msis), irq)) { + return ICS(&phb3->msis); + } } } return NULL; @@ -1990,28 +1984,22 @@ PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id) return NULL; } -static int pnv_ics_resend_child(Object *child, void *opaque) -{ - PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3); - - if (phb3) { - ics_resend(&phb3->lsis); - ics_resend(ICS(&phb3->msis)); - } - return 0; -} - static void pnv_ics_resend(XICSFabric *xi) { PnvMachineState *pnv = PNV_MACHINE(xi); - int i; + int i, j; for (i = 0; i < pnv->num_chips; i++) { - PnvChip *chip = pnv->chips[i]; Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); ics_resend(&chip8->psi.ics); - object_child_foreach(OBJECT(chip), pnv_ics_resend_child, NULL); + + for (j = 0; j < chip8->num_phbs; j++) { + PnvPHB3 *phb3 = &chip8->phbs[j]; + + ics_resend(&phb3->lsis); + ics_resend(ICS(&phb3->msis)); + } } } diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 83ecccca28d..62804082992 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -44,8 +44,8 @@ static void pnv_pnor_update(PnvPnor *s, int offset, int size) offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); - ret = blk_pwrite(s->blk, offset, s->storage + offset, - offset_end - offset, 0); + ret = blk_pwrite(s->blk, offset, offset_end - offset, s->storage + offset, + 0); if (ret < 0) { error_report("Could not update PNOR offset=0x%" PRIx32" : %s", offset, strerror(-ret)); @@ -99,7 +99,7 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) s->storage = blk_blockalign(s->blk, s->size); - if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { + if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index fea70df45e6..690f448cb94 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -154,10 +154,7 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) void ppc6xx_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu, - PPC6xx_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppc6xx_set_irq, PPC6xx_INPUT_NB); } #if defined(TARGET_PPC64) @@ -234,10 +231,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) void ppc970_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu, - PPC970_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppc970_set_irq, PPC970_INPUT_NB); } /* POWER7 internal IRQ controller */ @@ -260,10 +254,7 @@ static void power7_set_irq(void *opaque, int pin, int level) void ppcPOWER7_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, - POWER7_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), power7_set_irq, POWER7_INPUT_NB); } /* POWER9 internal IRQ controller */ @@ -292,10 +283,7 @@ static void power9_set_irq(void *opaque, int pin, int level) void ppcPOWER9_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu, - POWER9_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), power9_set_irq, POWER9_INPUT_NB); } #endif /* defined(TARGET_PPC64) */ @@ -431,10 +419,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) void ppc40x_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, - cpu, PPC40x_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppc40x_set_irq, PPC40x_INPUT_NB); } /* PowerPC E500 internal IRQ controller */ @@ -489,10 +474,7 @@ static void ppce500_set_irq(void *opaque, int pin, int level) void ppce500_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, - cpu, PPCE500_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppce500_set_irq, PPCE500_INPUT_NB); } /* Enable or Disable the E500 EPR capability */ diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 36c8ba6f3c1..d6420c88d3a 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -1470,9 +1470,9 @@ PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem, sysbus_realize_and_unref(uicsbd, &error_fatal); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); *uicdevp = uicdev; diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index d5973f2484e..873f930c77d 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -200,9 +200,9 @@ static void bamboo_init(MachineState *machine) sysbus_realize_and_unref(uicsbd, &error_fatal); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); /* SDRAM controller */ memset(ram_bases, 0, sizeof(ram_bases)); diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 993e3ba955d..a1ecf6dd1c2 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1180,6 +1180,14 @@ static void dcr_write_pcie(void *opaque, int dcrn, uint32_t val) case PEGPL_CFGMSK: s->cfg_mask = val; size = ~(val & 0xfffffffe) + 1; + /* + * Firmware sets this register to E0000001. Why we are not sure, + * but the current guess is anything above PCIE_MMCFG_SIZE_MAX is + * ignored. + */ + if (size > PCIE_MMCFG_SIZE_MAX) { + size = PCIE_MMCFG_SIZE_MAX; + } pcie_host_mmcfg_update(PCIE_HOST_BRIDGE(s), val & 1, s->cfg_base, size); break; case PEGPL_MSGBAH: diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index a1cd4505cc9..f08714f2ecb 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -275,7 +275,7 @@ static void ibm_40p_init(MachineState *machine) /* PCI -> ISA bridge */ i82378_dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378")); qdev_connect_gpio_out(i82378_dev, 0, - cpu->env.irq_inputs[PPC6xx_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(i82378_dev, 15)); isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0")); diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index 8c9b8dd67b7..5a56f155f50 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -262,7 +262,7 @@ static void prep_systemio_realize(DeviceState *dev, Error **errp) qemu_set_irq(s->non_contiguous_io_map_irq, s->iomap_type & PORT0850_IOMAP_NONCONTIGUOUS); cpu = POWERPC_CPU(first_cpu); - s->softreset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; + s->softreset_irq = qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_HRESET); isa_register_portio_list(isa, &s->portio, 0x0, ppc_io800_port_list, s, "systemio800"); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 2f24598f55d..7e8da657c2a 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -334,9 +334,9 @@ static void sam460ex_init(MachineState *machine) if (i == 0) { sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); } else { sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, qdev_get_gpio_in(uic[0], input_ints[i])); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index fd4942e8813..bc9ba6e6dcf 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qemu/memalign.h" +#include "qemu/guest-random.h" #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" @@ -898,6 +899,8 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) add_str(hypertas, "hcall-hpt-resize"); } + add_str(hypertas, "hcall-watchdog"); + _FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions", hypertas->str, hypertas->len)); g_string_free(hypertas, TRUE); @@ -1012,6 +1015,7 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) { MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + uint8_t rng_seed[32]; int chosen; _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); @@ -1089,6 +1093,9 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) spapr_dt_ov5_platform_support(spapr, fdt, chosen); } + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + _FDT(fdt_setprop(fdt, chosen, "rng-seed", rng_seed, sizeof(rng_seed))); + _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5")); } @@ -1329,6 +1336,11 @@ static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, 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) { @@ -3051,6 +3063,8 @@ static void spapr_machine_init(MachineState *machine) spapr->vof->fw_size = fw_size; /* for claim() on itself */ spapr_register_hypercall(KVMPPC_H_VOF_CLIENT, spapr_h_vof_client); } + + spapr_watchdog_init(spapr); } #define DEFAULT_KVM_TYPE "auto" diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 655ab856a01..b4283055c12 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -553,7 +553,7 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val, * instruction is a harmless no-op. It won't correctly * implement the cache count flush *but* if we have * count-cache-disabled in the host, that flush is - * unnnecessary. So, specifically allow this case. This + * unnecessary. So, specifically allow this case. This * allows us to have better performance on POWER9 DD2.3, * while still working on POWER9 DD2.2 and POWER8 host * cpus. diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index d761a7d0c34..a8d4a6bcf0e 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -920,6 +920,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, target_ulong page_size = args[2]; target_ulong table_size = args[3]; target_ulong update_lpcr = 0; + target_ulong table_byte_size; uint64_t cproc; if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */ @@ -927,6 +928,14 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, } if (flags & FLAG_MODIFY) { if (flags & FLAG_REGISTER) { + /* Check process table alignment */ + table_byte_size = 1ULL << (table_size + 12); + if (proc_tbl & (table_byte_size - 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: process table not properly aligned: proc_tbl 0x" + TARGET_FMT_lx" proc_tbl_size 0x"TARGET_FMT_lx"\n", + __func__, proc_tbl, table_byte_size); + } if (flags & FLAG_RADIX) { /* Register new RADIX process table */ if (proc_tbl & 0xfff || proc_tbl >> 60) { return H_P2; diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 81e5a1aea3a..63e34d457a0 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -279,7 +279,7 @@ static const VMStateDescription vmstate_spapr_tce_table_ex = { static const VMStateDescription vmstate_spapr_tce_table = { .name = "spapr_iommu", - .version_id = 2, + .version_id = 3, .minimum_version_id = 2, .pre_save = spapr_tce_table_pre_save, .post_load = spapr_tce_table_post_load, @@ -292,6 +292,7 @@ static const VMStateDescription vmstate_spapr_tce_table = { VMSTATE_BOOL(bypass, SpaprTceTable), VMSTATE_VARRAY_UINT32_ALLOC(mig_table, SpaprTceTable, mig_nb_table, 0, vmstate_info_uint64, uint64_t), + VMSTATE_BOOL_V(def_win, SpaprTceTable, 3), VMSTATE_END_OF_LIST() }, diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index b2f5fbef0c8..67e9d468aa9 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -2067,6 +2067,7 @@ void spapr_phb_dma_reset(SpaprPhbState *sphb) tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[0]); spapr_tce_table_enable(tcet, SPAPR_TCE_PAGE_SHIFT, sphb->dma_win_addr, sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT); + tcet->def_win = true; } static void spapr_phb_reset(DeviceState *qdev) @@ -2359,8 +2360,9 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, cpu_to_be32(RTAS_IBM_REMOVE_PE_DMA_WINDOW) }; uint32_t ddw_extensions[] = { - cpu_to_be32(1), - cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW) + cpu_to_be32(2), + cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW), + cpu_to_be32(1), /* 1: ibm,query-pe-dma-window 6 outputs, PAPR 2.8 */ }; SpaprTceTable *tcet; SpaprDrc *drc; diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index 13d339c807c..7ba11382bc3 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -100,7 +100,7 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, uint64_t buid; uint32_t avail, addr, pgmask = 0; - if ((nargs != 3) || (nret != 5)) { + if ((nargs != 3) || ((nret != 5) && (nret != 6))) { goto param_error_exit; } @@ -118,9 +118,20 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, avail); - rtas_st(rets, 2, 0x80000000); /* The largest window we can possibly have */ - rtas_st(rets, 3, pgmask); - rtas_st(rets, 4, 0); /* DMA migration mask, not supported */ + if (nret == 6) { + /* + * Set the Max TCE number as 1<<(58-21) = 0x20.0000.0000 + * 1<<59 is the huge window start and 21 is 2M page shift. + */ + rtas_st(rets, 2, 0x00000020); + rtas_st(rets, 3, 0x00000000); + rtas_st(rets, 4, pgmask); + rtas_st(rets, 5, 0); /* DMA migration mask, not supported */ + } else { + rtas_st(rets, 2, 0x80000000); + rtas_st(rets, 3, pgmask); + rtas_st(rets, 4, 0); /* DMA migration mask, not supported */ + } trace_spapr_iommu_ddw_query(buid, addr, avail, 0x80000000, pgmask); return; @@ -215,6 +226,7 @@ static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu, SpaprPhbState *sphb; SpaprTceTable *tcet; uint32_t liobn; + bool def_win_removed; if ((nargs != 1) || (nret != 1)) { goto param_error_exit; @@ -231,9 +243,23 @@ static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu, goto param_error_exit; } + def_win_removed = tcet->def_win; spapr_tce_table_disable(tcet); trace_spapr_iommu_ddw_remove(liobn); + /* + * PAPR+/LoPAPR says: + * The platform must restore the default DMA window for the PE on a call + * to the ibm,remove-pe-dma-window RTAS call when all of the following + * are true: + * a. The call removes the last DMA window remaining for the PE. + * b. The DMA window being removed is not the default window + */ + if (spapr_phb_get_active_win_num(sphb) == 0 && !def_win_removed) { + spapr_phb_dma_reset(sphb); + trace_spapr_iommu_ddw_reset(sphb->buid, 0); + } + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index b67a709ddce..53b126ff48a 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -111,9 +111,9 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) sysbus_realize_and_unref(uicsbd, &error_fatal); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); /* This board doesn't wire anything up to the inputs of the UIC. */ return cpu; @@ -213,7 +213,7 @@ static void virtex_init(MachineState *machine) CPUPPCState *env; hwaddr ram_base = 0; DriveInfo *dinfo; - qemu_irq irq[32], *cpu_irq; + qemu_irq irq[32], cpu_irq; int kernel_size; int i; @@ -236,12 +236,12 @@ static void virtex_init(MachineState *machine) dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); - cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; + cpu_irq = qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT); dev = qdev_new("xlnx.xps-intc"); qdev_prop_set_uint32(dev, "kind-of-intr", 0); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq[0]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 2d80f40b316..06b4fc5ac30 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -227,11 +227,11 @@ uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) /* * We should put fdt as far as possible to avoid kernel/initrd overwriting * its content. But it should be addressable by 32 bit system as well. - * Thus, put it at an 16MB aligned address that less than fdt size from the + * Thus, put it at an 2MB aligned address that less than fdt size from the * end of dram or 3GB whichever is lesser. */ temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; - fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 16 * MiB); + fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); ret = fdt_pack(fdt); /* Should only fail if we've built a corrupted tree */ diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index fe6710310f6..1f9e38a735b 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -72,9 +72,6 @@ struct LS7ARtcState { */ int64_t offset_toy; int64_t offset_rtc; - uint64_t save_toy_mon; - uint64_t save_toy_year; - uint64_t save_rtc; int64_t data; int tidx; uint32_t toymatch[3]; @@ -89,67 +86,45 @@ struct LS7ARtcState { }; /* switch nanoseconds time to rtc ticks */ -static inline uint64_t ls7a_rtc_ticks(void) +static uint64_t ls7a_rtc_ticks(void) { return qemu_clock_get_ns(rtc_clock) * LS7A_RTC_FREQ / NANOSECONDS_PER_SECOND; } /* switch rtc ticks to nanoseconds */ -static inline uint64_t ticks_to_ns(uint64_t ticks) +static uint64_t ticks_to_ns(uint64_t ticks) { return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ; } -static inline bool toy_enabled(LS7ARtcState *s) +static bool toy_enabled(LS7ARtcState *s) { return FIELD_EX32(s->cntrctl, RTC_CTRL, TOYEN) && FIELD_EX32(s->cntrctl, RTC_CTRL, EO); } -static inline bool rtc_enabled(LS7ARtcState *s) +static bool rtc_enabled(LS7ARtcState *s) { return FIELD_EX32(s->cntrctl, RTC_CTRL, RTCEN) && FIELD_EX32(s->cntrctl, RTC_CTRL, EO); } -/* parse toy value to struct tm */ -static inline void toy_val_to_time_mon(uint64_t toy_val, struct tm *tm) -{ - tm->tm_sec = FIELD_EX32(toy_val, TOY, SEC); - tm->tm_min = FIELD_EX32(toy_val, TOY, MIN); - tm->tm_hour = FIELD_EX32(toy_val, TOY, HOUR); - tm->tm_mday = FIELD_EX32(toy_val, TOY, DAY); - tm->tm_mon = FIELD_EX32(toy_val, TOY, MON) - 1; -} - -static inline void toy_val_to_time_year(uint64_t toy_year, struct tm *tm) -{ - tm->tm_year = toy_year; -} - /* parse struct tm to toy value */ -static inline uint64_t toy_time_to_val_mon(struct tm tm) +static uint64_t toy_time_to_val_mon(const struct tm *tm) { uint64_t val = 0; - val = FIELD_DP32(val, TOY, MON, tm.tm_mon + 1); - val = FIELD_DP32(val, TOY, DAY, tm.tm_mday); - val = FIELD_DP32(val, TOY, HOUR, tm.tm_hour); - val = FIELD_DP32(val, TOY, MIN, tm.tm_min); - val = FIELD_DP32(val, TOY, SEC, tm.tm_sec); + val = FIELD_DP32(val, TOY, MON, tm->tm_mon + 1); + val = FIELD_DP32(val, TOY, DAY, tm->tm_mday); + val = FIELD_DP32(val, TOY, HOUR, tm->tm_hour); + val = FIELD_DP32(val, TOY, MIN, tm->tm_min); + val = FIELD_DP32(val, TOY, SEC, tm->tm_sec); return val; } -static inline uint64_t toy_time_to_val_year(struct tm tm) -{ - uint64_t year; - - year = tm.tm_year; - return year; -} - -static inline void toymatch_val_to_time(uint64_t val, struct tm *tm) +static void toymatch_val_to_time(LS7ARtcState *s, uint64_t val, struct tm *tm) { + qemu_get_timedate(tm, s->offset_toy); tm->tm_sec = FIELD_EX32(val, TOY_MATCH, SEC); tm->tm_min = FIELD_EX32(val, TOY_MATCH, MIN); tm->tm_hour = FIELD_EX32(val, TOY_MATCH, HOUR); @@ -158,17 +133,18 @@ static inline void toymatch_val_to_time(uint64_t val, struct tm *tm) tm->tm_year += (FIELD_EX32(val, TOY_MATCH, YEAR) - (tm->tm_year & 0x3f)); } -static void toymatch_write(LS7ARtcState *s, struct tm *tm, uint64_t val, int num) +static void toymatch_write(LS7ARtcState *s, uint64_t val, int num) { int64_t now, expire_time; + struct tm tm = {}; /* it do not support write when toy disabled */ if (toy_enabled(s)) { s->toymatch[num] = val; - /* caculate expire time */ + /* calculate expire time */ now = qemu_clock_get_ms(rtc_clock); - toymatch_val_to_time(val, tm); - expire_time = now + (qemu_timedate_diff(tm) - s->offset_toy) * 1000; + toymatch_val_to_time(s, val, &tm); + expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; timer_mod(s->toy_timer[num], expire_time); } } @@ -180,7 +156,7 @@ static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) /* it do not support write when toy disabled */ if (rtc_enabled(s)) { s->rtcmatch[num] = val; - /* caculate expire time */ + /* calculate expire time */ expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc); timer_mod_ns(s->rtc_timer[num], expire_ns); } @@ -189,16 +165,8 @@ static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) static void ls7a_toy_stop(LS7ARtcState *s) { int i; - struct tm tm; - /* - * save time when disabled toy, - * because toy time not add counters. - */ - qemu_get_timedate(&tm, s->offset_toy); - s->save_toy_mon = toy_time_to_val_mon(tm); - s->save_toy_year = toy_time_to_val_year(tm); - /* delete timers, and when re-enabled, recaculate expire time */ + /* delete timers, and when re-enabled, recalculate expire time */ for (i = 0; i < TIMER_NUMS; i++) { timer_del(s->toy_timer[i]); } @@ -207,13 +175,8 @@ static void ls7a_toy_stop(LS7ARtcState *s) static void ls7a_rtc_stop(LS7ARtcState *s) { int i; - uint64_t time; - - /* save rtc time */ - time = ls7a_rtc_ticks() + s->offset_rtc; - s->save_rtc = time; - /* delete timers, and when re-enabled, recaculate expire time */ + /* delete timers, and when re-enabled, recalculate expire time */ for (i = 0; i < TIMER_NUMS; i++) { timer_del(s->rtc_timer[i]); } @@ -223,20 +186,13 @@ static void ls7a_toy_start(LS7ARtcState *s) { int i; uint64_t expire_time, now; - struct tm tm; - /* - * need to recaculate toy offset - * and expire time when enable it. - */ - toy_val_to_time_mon(s->save_toy_mon, &tm); - toy_val_to_time_year(s->save_toy_year, &tm); + struct tm tm = {}; - s->offset_toy = qemu_timedate_diff(&tm); now = qemu_clock_get_ms(rtc_clock); - /* recaculate expire time and enable timer */ + /* recalculate expire time and enable timer */ for (i = 0; i < TIMER_NUMS; i++) { - toymatch_val_to_time(s->toymatch[i], &tm); + toymatch_val_to_time(s, s->toymatch[i], &tm); expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; timer_mod(s->toy_timer[i], expire_time); } @@ -245,16 +201,9 @@ static void ls7a_toy_start(LS7ARtcState *s) static void ls7a_rtc_start(LS7ARtcState *s) { int i; - uint64_t expire_time, now; - - /* - * need to recaculate rtc offset - * and expire time when enable it. - */ - now = ls7a_rtc_ticks(); - s->offset_rtc = s->save_rtc - now; + uint64_t expire_time; - /* recaculate expire time and enable timer */ + /* recalculate expire time and enable timer */ for (i = 0; i < TIMER_NUMS; i++) { expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc); timer_mod_ns(s->rtc_timer[i], expire_time); @@ -269,23 +218,21 @@ static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { case SYS_TOYREAD0: - /* if toy disabled, read save toy time */ if (toy_enabled(s)) { qemu_get_timedate(&tm, s->offset_toy); - val = toy_time_to_val_mon(tm); + val = toy_time_to_val_mon(&tm); } else { - /* read save mon val */ - val = s->save_toy_mon; + /* return 0 when toy disabled */ + val = 0; } break; case SYS_TOYREAD1: - /* if toy disabled, read save toy time */ if (toy_enabled(s)) { qemu_get_timedate(&tm, s->offset_toy); val = tm.tm_year; } else { - /* read save year val */ - val = s->save_toy_year; + /* return 0 when toy disabled */ + val = 0; } break; case SYS_TOYMATCH0: @@ -301,11 +248,11 @@ static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size) val = s->cntrctl; break; case SYS_RTCREAD0: - /* if rtc disabled, read save rtc time */ if (rtc_enabled(s)) { val = ls7a_rtc_ticks() + s->offset_rtc; } else { - val = s->save_rtc; + /* return 0 when rtc disabled */ + val = 0; } break; case SYS_RTCMATCH0: @@ -352,13 +299,13 @@ static void ls7a_rtc_write(void *opaque, hwaddr addr, } break; case SYS_TOYMATCH0: - toymatch_write(s, &tm, val, 0); + toymatch_write(s, val, 0); break; case SYS_TOYMATCH1: - toymatch_write(s, &tm, val, 1); + toymatch_write(s, val, 1); break; case SYS_TOYMATCH2: - toymatch_write(s, &tm, val, 2); + toymatch_write(s, val, 2); break; case SYS_RTCCTRL: /* get old ctrl */ @@ -423,7 +370,7 @@ static void toy_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (toy_enabled(s)) { - qemu_irq_pulse(s->irq); + qemu_irq_raise(s->irq); } } @@ -432,7 +379,7 @@ static void rtc_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (rtc_enabled(s)) { - qemu_irq_pulse(s->irq); + qemu_irq_raise(s->irq); } } @@ -455,11 +402,26 @@ static void ls7a_rtc_realize(DeviceState *dev, Error **errp) } d->offset_toy = 0; d->offset_rtc = 0; - d->save_toy_mon = 0; - d->save_toy_year = 0; - d->save_rtc = 0; - create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4); +} + +/* delete timer and clear reg when reset */ +static void ls7a_rtc_reset(DeviceState *dev) +{ + int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + LS7ARtcState *d = LS7A_RTC(sbd); + for (i = 0; i < TIMER_NUMS; i++) { + if (toy_enabled(d)) { + timer_del(d->toy_timer[i]); + } + if (rtc_enabled(d)) { + timer_del(d->rtc_timer[i]); + } + d->toymatch[i] = 0; + d->rtcmatch[i] = 0; + } + d->cntrctl = 0; } static int ls7a_rtc_pre_save(void *opaque) @@ -495,9 +457,6 @@ static const VMStateDescription vmstate_ls7a_rtc = { .fields = (VMStateField[]) { VMSTATE_INT64(offset_toy, LS7ARtcState), VMSTATE_INT64(offset_rtc, LS7ARtcState), - VMSTATE_UINT64(save_toy_mon, LS7ARtcState), - VMSTATE_UINT64(save_toy_year, LS7ARtcState), - VMSTATE_UINT64(save_rtc, LS7ARtcState), VMSTATE_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS), VMSTATE_UINT32_ARRAY(rtcmatch, LS7ARtcState, TIMER_NUMS), VMSTATE_UINT32(cntrctl, LS7ARtcState), @@ -510,6 +469,7 @@ static void ls7a_rtc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_ls7a_rtc; dc->realize = ls7a_rtc_realize; + dc->reset = ls7a_rtc_reset; dc->desc = "ls7a rtc"; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 15b458527e0..e33e5207ab8 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -249,12 +249,11 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, return 0; } -static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) +static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev) { CcwDevice *ccw_dev = CCW_DEVICE(dev); - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_bus_reset(&dev->bus); if (dev->indicators) { release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; @@ -359,7 +358,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); break; case CCW_CMD_VDEV_RESET: - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -536,7 +535,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } if (virtio_set_status(vdev, status) == 0) { if (vdev->status == 0) { - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -921,10 +920,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); if (vdc->parent_reset) { vdc->parent_reset(d); } diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index c8773f73f75..ad5f5e5f394 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -1028,8 +1028,9 @@ static void lsi_do_msgout(LSIState *s) case 0x0d: /* The ABORT TAG message clears the current I/O process only. */ trace_lsi_do_msgout_abort(current_tag); - if (current_req) { + if (current_req && current_req->req) { scsi_req_cancel(current_req->req); + current_req = NULL; } lsi_disconnect(s); break; @@ -1055,6 +1056,7 @@ static void lsi_do_msgout(LSIState *s) /* clear the current I/O process */ if (s->current) { scsi_req_cancel(s->current->req); + current_req = NULL; } /* As the current implemented devices scsi_disk and scsi_generic diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 072686ed58f..f5cdb9ad4b5 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -94,6 +94,7 @@ struct SCSIDiskState { uint16_t port_index; uint64_t max_unmap_size; uint64_t max_io_size; + uint32_t quirks; QEMUBH *bh; char *version; char *serial; @@ -1078,12 +1079,14 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, int page_control) { static const int mode_sense_valid[0x3f] = { + [MODE_PAGE_VENDOR_SPECIFIC] = (1 << TYPE_DISK) | (1 << TYPE_ROM), [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM), [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), + [MODE_PAGE_APPLE_VENDOR] = (1 << TYPE_ROM), }; uint8_t *p = *p_outbuf + 2; @@ -1185,6 +1188,10 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, case MODE_PAGE_R_W_ERROR: length = 10; if (page_control == 1) { /* Changeable Values */ + if (s->qdev.type == TYPE_ROM) { + /* Automatic Write Reallocation Enabled */ + p[0] = 0x80; + } break; } p[0] = 0x80; /* Automatic Write Reallocation Enabled */ @@ -1228,6 +1235,36 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[19] = (16 * 176) & 0xff; break; + case MODE_PAGE_APPLE_VENDOR: + if (s->quirks & (1 << SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR)) { + length = 0x1e; + if (page_control == 1) { /* Changeable Values */ + break; + } + + memset(p, 0, length); + strcpy((char *)p + 8, "APPLE COMPUTER, INC "); + break; + } else { + return -1; + } + + case MODE_PAGE_VENDOR_SPECIFIC: + if (s->qdev.type == TYPE_DISK && (s->quirks & + (1 << SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE))) { + length = 0x2; + if (page_control == 1) { /* Changeable Values */ + p[0] = 0xff; + p[1] = 0xff; + break; + } + p[0] = 0; + p[1] = 0; + break; + } else { + return -1; + } + default: return -1; } @@ -1263,10 +1300,27 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) dev_specific_param |= 0x80; /* Readonly. */ } } else { - /* MMC prescribes that CD/DVD drives have no block descriptors, - * and defines no device-specific parameter. */ - dev_specific_param = 0x00; - dbd = true; + if (s->quirks & (1 << SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD)) { + /* Use DBD from the request... */ + dev_specific_param = 0x00; + + /* + * ... unless we receive a request for MODE_PAGE_APPLE_VENDOR + * which should never return a block descriptor even though DBD is + * not set, otherwise CDROM detection fails in MacOS + */ + if (s->quirks & (1 << SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR) && + page == MODE_PAGE_APPLE_VENDOR) { + dbd = true; + } + } else { + /* + * MMC prescribes that CD/DVD drives have no block descriptors, + * and defines no device-specific parameter. + */ + dev_specific_param = 0x00; + dbd = true; + } } if (r->req.cmd.buf[0] == MODE_SENSE) { @@ -1502,7 +1556,10 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param; } if (page_len > len) { - goto invalid_param_len; + if (!(s->quirks & SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED)) { + goto invalid_param_len; + } + trace_scsi_disk_mode_select_page_truncated(page, page_len, len); } if (!change) { @@ -1537,9 +1594,12 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) int bd_len; int pass; - /* We only support PF=1, SP=0. */ if ((r->req.cmd.buf[1] & 0x11) != 0x10) { - goto invalid_field; + if (!(s->quirks & + (1 << SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE))) { + /* We only support PF=1, SP=0. */ + goto invalid_field; + } } if (len < hdr_len) { @@ -1556,6 +1616,12 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) goto invalid_param; } + /* Allow changing the block size */ + if (bd_len && p[6] != (s->qdev.blocksize >> 8)) { + s->qdev.blocksize = p[6] << 8; + trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize); + } + len -= bd_len; p += bd_len; @@ -2127,6 +2193,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) trace_scsi_disk_emulate_command_WRITE_SAME( req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, r->req.cmd.xfer); break; + case FORMAT_UNIT: + trace_scsi_disk_emulate_command_FORMAT_UNIT(r->req.cmd.xfer); + break; default: trace_scsi_disk_emulate_command_UNKNOWN(buf[0], scsi_command_name(buf[0])); @@ -2419,7 +2488,6 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) } else { blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_block_ops, s); } - blk_set_guest_block_size(s->qdev.conf.blk, s->qdev.blocksize); blk_iostatus_enable(s->qdev.conf.blk); @@ -2533,6 +2601,7 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [VERIFY_10] = &scsi_disk_emulate_reqops, [VERIFY_12] = &scsi_disk_emulate_reqops, [VERIFY_16] = &scsi_disk_emulate_reqops, + [FORMAT_UNIT] = &scsi_disk_emulate_reqops, [READ_6] = &scsi_disk_dma_reqops, [READ_10] = &scsi_disk_dma_reqops, @@ -3037,6 +3106,9 @@ static Property scsi_hd_properties[] = { DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, 5), + DEFINE_PROP_BIT("quirk_mode_page_vendor_specific_apple", SCSIDiskState, + quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE, + 0), DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_END_OF_LIST(), }; @@ -3085,6 +3157,15 @@ static Property scsi_cd_properties[] = { DEFAULT_MAX_IO_SIZE), DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, 5), + DEFINE_PROP_BIT("quirk_mode_page_apple_vendor", SCSIDiskState, quirks, + SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR, 0), + DEFINE_PROP_BIT("quirk_mode_sense_rom_use_dbd", SCSIDiskState, quirks, + SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD, 0), + DEFINE_PROP_BIT("quirk_mode_page_vendor_specific_apple", SCSIDiskState, + quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE, + 0), + DEFINE_PROP_BIT("quirk_mode_page_truncated", SCSIDiskState, quirks, + SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 0ab00ef85c9..ada24d74869 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -321,7 +321,6 @@ static void scsi_read_complete(void * opaque, int ret) s->blocksize = ldl_be_p(&r->buf[8]); s->max_lba = ldq_be_p(&r->buf[0]); } - blk_set_guest_block_size(s->conf.blk, s->blocksize); /* * Patch MODE SENSE device specific parameters if the BDS is opened diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index a07a8e1523f..e320ccaa238 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1013,7 +1013,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) } /* - * Current implementation does not suppport any migration or + * Current implementation does not support any migration or * reservation capabilities. Construct the response telling the * guest not to use them. */ diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index 20fb0dc1623..ab238293f0d 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -334,10 +334,13 @@ scsi_disk_emulate_command_UNMAP(size_t xfer) "Unmap (len %zd)" scsi_disk_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)" scsi_disk_emulate_command_WRITE_SAME(int cmd, size_t xfer) "WRITE SAME %d (len %zd)" scsi_disk_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)" +scsi_disk_emulate_command_FORMAT_UNIT(size_t xfer) "Format Unit (len %zu)" scsi_disk_dma_command_READ(uint64_t lba, uint32_t len) "Read (sector %" PRId64 ", count %u)" scsi_disk_dma_command_WRITE(const char *cmd, uint64_t lba, int len) "Write %s(sector %" PRId64 ", count %u)" scsi_disk_new_request(uint32_t lun, uint32_t tag, const char *line) "Command: lun=%d tag=0x%x data=%s" scsi_disk_aio_sgio_command(uint32_t tag, uint8_t cmd, uint64_t lba, int len, uint32_t timeout) "disk aio sgio: tag=0x%x cmd=0x%x (sector %" PRId64 ", count %d) timeout=%u" +scsi_disk_mode_select_page_truncated(int page, int len, int page_len) "page %d expected length %d but received length %d" +scsi_disk_mode_select_set_blocksize(int blocksize) "set block size to %d" # scsi-generic.c scsi_generic_command_complete_noio(void *req, uint32_t tag, int statuc) "Command complete %p tag=0x%x status=%d" diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8e6fa091513..da5bdd134a9 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -752,7 +752,7 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_read_block(addr, len); - if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) { + if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } } @@ -760,7 +760,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_write_block(addr, len); - if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) { + if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } } diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index df392e78690..e03bd09b50e 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -34,3 +34,7 @@ config LSM303DLHC_MAG config ISL_PMBUS_VR bool depends on PMBUS + +config MAX31785 + bool + depends on PMBUS diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index e11e0288840..eb344dd5a9d 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -15,6 +15,18 @@ static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev) { + ISLState *s = ISL69260(pmdev); + + switch (pmdev->code) { + case PMBUS_IC_DEVICE_ID: + if (!s->ic_device_id_len) { + break; + } + pmbus_send(pmdev, s->ic_device_id, s->ic_device_id_len); + pmbus_idle(pmdev); + return 0; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: reading from unsupported register: 0x%02x\n", __func__, pmdev->code); @@ -107,6 +119,18 @@ static void raa228000_exit_reset(Object *obj) pmdev->pages[0].read_temperature_3 = 0; } +static void isl69259_exit_reset(Object *obj) +{ + ISLState *s = ISL69260(obj); + static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 0x3c}; + g_assert(sizeof(ic_device_id) <= sizeof(s->ic_device_id)); + + isl_pmbus_vr_exit_reset(obj); + + s->ic_device_id_len = sizeof(ic_device_id); + memcpy(s->ic_device_id, ic_device_id, sizeof(ic_device_id)); +} + static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -245,6 +269,21 @@ static void raa229004_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 2); } +static void isl69259_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "Renesas ISL69259 Digital Multiphase Voltage Regulator"; + rc->phases.exit = isl69259_exit_reset; + isl_pmbus_vr_class_init(klass, data, 2); +} + +static const TypeInfo isl69259_info = { + .name = TYPE_ISL69259, + .parent = TYPE_ISL69260, + .class_init = isl69259_class_init, +}; + static const TypeInfo isl69260_info = { .name = TYPE_ISL69260, .parent = TYPE_PMBUS_DEVICE, @@ -271,6 +310,7 @@ static const TypeInfo raa228000_info = { static void isl_pmbus_vr_register_types(void) { + type_register_static(&isl69259_info); type_register_static(&isl69260_info); type_register_static(&raa228000_info); type_register_static(&raa229004_info); diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index 4c98ddbf207..bb8d48b2fdb 100644 --- a/hw/sensor/lsm303dlhc_mag.c +++ b/hw/sensor/lsm303dlhc_mag.c @@ -427,6 +427,8 @@ static int lsm303dlhc_mag_event(I2CSlave *i2c, enum i2c_event event) break; case I2C_NACK: break; + default: + return -1; } s->len = 0; diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c new file mode 100644 index 00000000000..8b95e324814 --- /dev/null +++ b/hw/sensor/max31785.c @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Maxim MAX31785 PMBus 6-Channel Fan Controller + * + * Datasheet: + * https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf + * + * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "qemu/osdep.h" +#include "hw/i2c/pmbus_device.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define TYPE_MAX31785 "max31785" +#define MAX31785(obj) OBJECT_CHECK(MAX31785State, (obj), TYPE_MAX31785) + +/* MAX31785 mfr specific PMBus commands */ +#define MAX31785_MFR_MODE 0xD1 +#define MAX31785_MFR_PSEN_CONFIG 0xD2 +#define MAX31785_MFR_VOUT_PEAK 0xD4 +#define MAX31785_MFR_TEMPERATURE_PEAK 0xD6 +#define MAX31785_MFR_VOUT_MIN 0xD7 +#define MAX31785_MFR_FAULT_RESPONSE 0xD9 +#define MAX31785_MFR_NV_FAULT_LOG 0xDC +#define MAX31785_MFR_TIME_COUNT 0xDD +#define MAX31785_MFR_TEMP_SENSOR_CONFIG 0xF0 +#define MAX31785_MFR_FAN_CONFIG 0xF1 +#define MAX31785_MFR_FAN_LUT 0xF2 +#define MAX31785_MFR_READ_FAN_PWM 0xF3 +#define MAX31785_MFR_FAN_FAULT_LIMIT 0xF5 +#define MAX31785_MFR_FAN_WARN_LIMIT 0xF6 +#define MAX31785_MFR_FAN_RUN_TIME 0xF7 +#define MAX31785_MFR_FAN_PWM_AVG 0xF8 +#define MAX31785_MFR_FAN_PWM2RPM 0xF9 + +/* defaults as per the data sheet */ +#define MAX31785_DEFAULT_CAPABILITY 0x10 +#define MAX31785_DEFAULT_VOUT_MODE 0x40 +#define MAX31785_DEFAULT_VOUT_SCALE_MONITOR 0x7FFF +#define MAX31785_DEFAULT_FAN_COMMAND_1 0x7FFF +#define MAX31785_DEFAULT_OV_FAULT_LIMIT 0x7FFF +#define MAX31785_DEFAULT_OV_WARN_LIMIT 0x7FFF +#define MAX31785_DEFAULT_OT_FAULT_LIMIT 0x7FFF +#define MAX31785_DEFAULT_OT_WARN_LIMIT 0x7FFF +#define MAX31785_DEFAULT_PMBUS_REVISION 0x11 +#define MAX31785_DEFAULT_MFR_ID 0x4D +#define MAX31785_DEFAULT_MFR_MODEL 0x53 +#define MAX31785_DEFAULT_MFR_REVISION 0x3030 +#define MAX31785A_DEFAULT_MFR_REVISION 0x3040 +#define MAX31785B_DEFAULT_MFR_REVISION 0x3061 +#define MAX31785B_DEFAULT_MFR_TEMPERATURE_PEAK 0x8000 +#define MAX31785B_DEFAULT_MFR_VOUT_MIN 0x7FFF +#define MAX31785_DEFAULT_TEXT 0x3130313031303130 + +/* MAX31785 pages */ +#define MAX31785_TOTAL_NUM_PAGES 23 +#define MAX31785_FAN_PAGES 6 +#define MAX31785_MIN_FAN_PAGE 0 +#define MAX31785_MAX_FAN_PAGE 5 +#define MAX31785_MIN_TEMP_PAGE 6 +#define MAX31785_MAX_TEMP_PAGE 16 +#define MAX31785_MIN_ADC_VOLTAGE_PAGE 17 +#define MAX31785_MAX_ADC_VOLTAGE_PAGE 22 + +/* FAN_CONFIG_1_2 */ +#define MAX31785_MFR_FAN_CONFIG 0xF1 +#define MAX31785_FAN_CONFIG_ENABLE BIT(7) +#define MAX31785_FAN_CONFIG_RPM_PWM BIT(6) +#define MAX31785_FAN_CONFIG_PULSE(pulse) (pulse << 4) +#define MAX31785_DEFAULT_FAN_CONFIG_1_2(pulse) \ + (MAX31785_FAN_CONFIG_ENABLE | MAX31785_FAN_CONFIG_PULSE(pulse)) +#define MAX31785_DEFAULT_MFR_FAN_CONFIG 0x0000 + +/* fan speed in RPM */ +#define MAX31785_DEFAULT_FAN_SPEED 0x7fff +#define MAX31785_DEFAULT_FAN_STATUS 0x00 + +#define MAX31785_DEFAULT_FAN_MAX_PWM 0x2710 + +/* + * MAX31785State: + * @code: The command code received + * @page: Each page corresponds to a device monitored by the Max 31785 + * The page register determines the available commands depending on device + * _____________________________________________________________________________ + * | 0 | Fan Connected to PWM0 | + * |_______|___________________________________________________________________| + * | 1 | Fan Connected to PWM1 | + * |_______|___________________________________________________________________| + * | 2 | Fan Connected to PWM2 | + * |_______|___________________________________________________________________| + * | 3 | Fan Connected to PWM3 | + * |_______|___________________________________________________________________| + * | 4 | Fan Connected to PWM4 | + * |_______|___________________________________________________________________| + * | 5 | Fan Connected to PWM5 | + * |_______|___________________________________________________________________| + * | 6 | Remote Thermal Diode Connected to ADC 0 | + * |_______|___________________________________________________________________| + * | 7 | Remote Thermal Diode Connected to ADC 1 | + * |_______|___________________________________________________________________| + * | 8 | Remote Thermal Diode Connected to ADC 2 | + * |_______|___________________________________________________________________| + * | 9 | Remote Thermal Diode Connected to ADC 3 | + * |_______|___________________________________________________________________| + * | 10 | Remote Thermal Diode Connected to ADC 4 | + * |_______|___________________________________________________________________| + * | 11 | Remote Thermal Diode Connected to ADC 5 | + * |_______|___________________________________________________________________| + * | 12 | Internal Temperature Sensor | + * |_______|___________________________________________________________________| + * | 13 | Remote I2C Temperature Sensor with Address 0 | + * |_______|___________________________________________________________________| + * | 14 | Remote I2C Temperature Sensor with Address 1 | + * |_______|___________________________________________________________________| + * | 15 | Remote I2C Temperature Sensor with Address 2 | + * |_______|___________________________________________________________________| + * | 16 | Remote I2C Temperature Sensor with Address 3 | + * |_______|___________________________________________________________________| + * | 17 | Remote I2C Temperature Sensor with Address 4 | + * |_______|___________________________________________________________________| + * | 17 | Remote Voltage Connected to ADC0 | + * |_______|___________________________________________________________________| + * | 18 | Remote Voltage Connected to ADC1 | + * |_______|___________________________________________________________________| + * | 19 | Remote Voltage Connected to ADC2 | + * |_______|___________________________________________________________________| + * | 20 | Remote Voltage Connected to ADC3 | + * |_______|___________________________________________________________________| + * | 21 | Remote Voltage Connected to ADC4 | + * |_______|___________________________________________________________________| + * | 22 | Remote Voltage Connected to ADC5 | + * |_______|___________________________________________________________________| + * |23-254 | Reserved | + * |_______|___________________________________________________________________| + * | 255 | Applies to all pages | + * |_______|___________________________________________________________________| + */ + +/* Place holder to save the max31785 mfr specific registers */ +typedef struct MAX31785State { + PMBusDevice parent; + uint16_t mfr_mode[MAX31785_TOTAL_NUM_PAGES]; + uint16_t vout_peak[MAX31785_TOTAL_NUM_PAGES]; + uint16_t temperature_peak[MAX31785_TOTAL_NUM_PAGES]; + uint16_t vout_min[MAX31785_TOTAL_NUM_PAGES]; + uint8_t fault_response[MAX31785_TOTAL_NUM_PAGES]; + uint32_t time_count[MAX31785_TOTAL_NUM_PAGES]; + uint16_t temp_sensor_config[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_config[MAX31785_TOTAL_NUM_PAGES]; + uint16_t read_fan_pwm[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_fault_limit[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_warn_limit[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_run_time[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_pwm_avg[MAX31785_TOTAL_NUM_PAGES]; + uint64_t fan_pwm2rpm[MAX31785_TOTAL_NUM_PAGES]; + uint64_t mfr_location; + uint64_t mfr_date; + uint64_t mfr_serial; + uint16_t mfr_revision; +} MAX31785State; + +static uint8_t max31785_read_byte(PMBusDevice *pmdev) +{ + MAX31785State *s = MAX31785(pmdev); + switch (pmdev->code) { + + case PMBUS_FAN_CONFIG_1_2: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send8(pmdev, pmdev->pages[pmdev->page].fan_config_1_2); + } + break; + + case PMBUS_FAN_COMMAND_1: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, pmdev->pages[pmdev->page].fan_command_1); + } + break; + + case PMBUS_READ_FAN_SPEED_1: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, pmdev->pages[pmdev->page].read_fan_speed_1); + } + break; + + case PMBUS_STATUS_FANS_1_2: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, pmdev->pages[pmdev->page].status_fans_1_2); + } + break; + + case PMBUS_MFR_REVISION: + pmbus_send16(pmdev, MAX31785_DEFAULT_MFR_REVISION); + break; + + case PMBUS_MFR_ID: + pmbus_send8(pmdev, 0x4d); /* Maxim */ + break; + + case PMBUS_MFR_MODEL: + pmbus_send8(pmdev, 0x53); + break; + + case PMBUS_MFR_LOCATION: + pmbus_send64(pmdev, s->mfr_location); + break; + + case PMBUS_MFR_DATE: + pmbus_send64(pmdev, s->mfr_date); + break; + + case PMBUS_MFR_SERIAL: + pmbus_send64(pmdev, s->mfr_serial); + break; + + case MAX31785_MFR_MODE: + pmbus_send16(pmdev, s->mfr_mode[pmdev->page]); + break; + + case MAX31785_MFR_VOUT_PEAK: + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + pmbus_send16(pmdev, s->vout_peak[pmdev->page]); + } + break; + + case MAX31785_MFR_TEMPERATURE_PEAK: + if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) && + (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) { + pmbus_send16(pmdev, s->temperature_peak[pmdev->page]); + } + break; + + case MAX31785_MFR_VOUT_MIN: + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + pmbus_send16(pmdev, s->vout_min[pmdev->page]); + } + break; + + case MAX31785_MFR_FAULT_RESPONSE: + pmbus_send8(pmdev, s->fault_response[pmdev->page]); + break; + + case MAX31785_MFR_TIME_COUNT: /* R/W 32 */ + pmbus_send32(pmdev, s->time_count[pmdev->page]); + break; + + case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */ + if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) && + (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) { + pmbus_send16(pmdev, s->temp_sensor_config[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_config[pmdev->page]); + } + break; + + case MAX31785_MFR_READ_FAN_PWM: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->read_fan_pwm[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_fault_limit[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_warn_limit[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_run_time[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_pwm_avg[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send64(pmdev, s->fan_pwm2rpm[pmdev->page]); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + + return 0xFF; +} + +static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + MAX31785State *s = MAX31785(pmdev); + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + pmdev->code = buf[0]; /* PMBus command code */ + + if (len == 1) { + return 0; + } + + /* Exclude command code from buffer */ + buf++; + len--; + + switch (pmdev->code) { + + case PMBUS_FAN_CONFIG_1_2: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmdev->pages[pmdev->page].fan_config_1_2 = pmbus_receive8(pmdev); + } + break; + + case PMBUS_FAN_COMMAND_1: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmdev->pages[pmdev->page].fan_command_1 = pmbus_receive16(pmdev); + pmdev->pages[pmdev->page].read_fan_speed_1 = + ((MAX31785_DEFAULT_FAN_SPEED / MAX31785_DEFAULT_FAN_MAX_PWM) * + pmdev->pages[pmdev->page].fan_command_1); + } + break; + + case PMBUS_MFR_LOCATION: /* R/W 64 */ + s->mfr_location = pmbus_receive64(pmdev); + break; + + case PMBUS_MFR_DATE: /* R/W 64 */ + s->mfr_date = pmbus_receive64(pmdev); + break; + + case PMBUS_MFR_SERIAL: /* R/W 64 */ + s->mfr_serial = pmbus_receive64(pmdev); + break; + + case MAX31785_MFR_MODE: /* R/W word */ + s->mfr_mode[pmdev->page] = pmbus_receive16(pmdev); + break; + + case MAX31785_MFR_VOUT_PEAK: /* R/W word */ + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + s->vout_peak[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_TEMPERATURE_PEAK: /* R/W word */ + if ((pmdev->page >= 6) && (pmdev->page <= 16)) { + s->temperature_peak[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_VOUT_MIN: /* R/W word */ + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + s->vout_min[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAULT_RESPONSE: /* R/W 8 */ + s->fault_response[pmdev->page] = pmbus_receive8(pmdev); + break; + + case MAX31785_MFR_TIME_COUNT: /* R/W 32 */ + s->time_count[pmdev->page] = pmbus_receive32(pmdev); + break; + + case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */ + if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) && + (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) { + s->temp_sensor_config[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_config[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_fault_limit[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_warn_limit[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_run_time[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_pwm_avg[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_pwm2rpm[pmdev->page] = pmbus_receive64(pmdev); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: writing to unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + + return 0; +} + +static void max31785_exit_reset(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + MAX31785State *s = MAX31785(obj); + + pmdev->capability = MAX31785_DEFAULT_CAPABILITY; + + for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) { + pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE; + pmdev->pages[i].fan_command_1 = MAX31785_DEFAULT_FAN_COMMAND_1; + pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION; + pmdev->pages[i].fan_config_1_2 = MAX31785_DEFAULT_FAN_CONFIG_1_2(0); + pmdev->pages[i].read_fan_speed_1 = MAX31785_DEFAULT_FAN_SPEED; + pmdev->pages[i].status_fans_1_2 = MAX31785_DEFAULT_FAN_STATUS; + } + + for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) { + pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE; + pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION; + pmdev->pages[i].ot_fault_limit = MAX31785_DEFAULT_OT_FAULT_LIMIT; + pmdev->pages[i].ot_warn_limit = MAX31785_DEFAULT_OT_WARN_LIMIT; + } + + for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE; + i <= MAX31785_MAX_ADC_VOLTAGE_PAGE; + i++) { + pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE; + pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION; + pmdev->pages[i].vout_scale_monitor = + MAX31785_DEFAULT_VOUT_SCALE_MONITOR; + pmdev->pages[i].vout_ov_fault_limit = MAX31785_DEFAULT_OV_FAULT_LIMIT; + pmdev->pages[i].vout_ov_warn_limit = MAX31785_DEFAULT_OV_WARN_LIMIT; + } + + s->mfr_location = MAX31785_DEFAULT_TEXT; + s->mfr_date = MAX31785_DEFAULT_TEXT; + s->mfr_serial = MAX31785_DEFAULT_TEXT; +} + +static const VMStateDescription vmstate_max31785 = { + .name = TYPE_MAX31785, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]){ + VMSTATE_PMBUS_DEVICE(parent, MAX31785State), + VMSTATE_UINT16_ARRAY(mfr_mode, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(vout_peak, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(temperature_peak, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(vout_min, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT8_ARRAY(fault_response, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT32_ARRAY(time_count, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_config, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(read_fan_pwm, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_fault_limit, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_warn_limit, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_run_time, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_pwm_avg, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT64_ARRAY(fan_pwm2rpm, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT64(mfr_location, MAX31785State), + VMSTATE_UINT64(mfr_date, MAX31785State), + VMSTATE_UINT64(mfr_serial, MAX31785State), + VMSTATE_END_OF_LIST() + } +}; + +static void max31785_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) { + pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE); + } + + for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) { + pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_TEMPERATURE); + } + + for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE; + i <= MAX31785_MAX_ADC_VOLTAGE_PAGE; + i++) { + pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_VOUT | + PB_HAS_VOUT_RATING); + } +} + +static void max31785_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + dc->desc = "Maxim MAX31785 6-Channel Fan Controller"; + dc->vmsd = &vmstate_max31785; + k->write_data = max31785_write_data; + k->receive_byte = max31785_read_byte; + k->device_num_pages = MAX31785_TOTAL_NUM_PAGES; + rc->phases.exit = max31785_exit_reset; +} + +static const TypeInfo max31785_info = { + .name = TYPE_MAX31785, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(MAX31785State), + .instance_init = max31785_init, + .class_init = max31785_class_init, +}; + +static void max31785_register_types(void) +{ + type_register_static(&max31785_info); +} + +type_init(max31785_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 12b6992bc84..9e9be602c34 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -6,3 +6,4 @@ softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) softmmu_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) softmmu_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c')) +softmmu_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c')) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 68aa697164d..faed7e0cbe1 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -488,7 +488,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) switch (aspeed_smc_flash_mode(fl)) { case CTRL_USERMODE: for (i = 0; i < size; i++) { - ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); } break; case CTRL_READMODE: @@ -497,7 +497,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { - ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); } aspeed_smc_flash_unselect(fl); diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c index 4a08b1cbd77..bbc5da07b58 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -56,7 +56,6 @@ static const USBDesc desc_canokey = { .iSerialNumber = STR_SERIALNUMBER, }, .full = &desc_device_canokey, - .high = &desc_device_canokey, .str = desc_strings, }; @@ -103,17 +102,23 @@ int canokey_emu_transmit( pbuf, size); key->ep_in_size[ep_in] += size; key->ep_in_state[ep_in] = CANOKEY_EP_IN_READY; + /* + * wake up controller if we NAKed IN token before + * Note: this is a quirk for CanoKey CTAPHID + */ + if (ep_in == CANOKEY_EMU_EP_CTAPHID) { + usb_wakeup(usb_ep_get(&key->dev, USB_TOKEN_IN, ep_in), 0); + } /* * ready for more data in device loop * * Note: this is a quirk for CanoKey CTAPHID * because it calls multiple emu_transmit in one device_loop * but w/o data_in it would stuck in device_loop - * This has no side effect for CCID as it is strictly - * OUT then IN transfer - * However it has side effect for Control transfer + * This has side effect for CCID since CCID can send ZLP + * This also has side effect for Control transfer */ - if (ep_in != 0) { + if (ep_in == CANOKEY_EMU_EP_CTAPHID) { canokey_emu_data_in(ep_in); } return 0; @@ -209,6 +214,22 @@ static void canokey_handle_data(USBDevice *dev, USBPacket *p) key->ep_out_size[ep_out] = out_len; canokey_emu_data_out(ep_out, NULL); } + /* + * Note: this is a quirk for CanoKey CTAPHID + * + * There is one code path that uses this device loop + * INTR IN -> useful data_in and useless device_loop -> NAKed + * INTR OUT -> useful device loop -> transmit -> wakeup + * (useful thanks to both data_in and data_out having been called) + * the next INTR IN -> actual data to guest + * + * if there is no such device loop, there would be no further + * INTR IN, no device loop, no transmit hence no usb_wakeup + * then qemu would hang + */ + if (ep_in == CANOKEY_EMU_EP_CTAPHID) { + canokey_emu_device_loop(); /* may call transmit multiple times */ + } break; case USB_TOKEN_IN: if (key->ep_in_pos[ep_in] == 0) { /* first time IN */ diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 0cd0a5e5402..296cc6c8e69 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3269,7 +3269,8 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, DPRINTF("%s\n", __func__); slotid = ep->dev->addr; - if (slotid == 0 || !xhci->slots[slotid-1].enabled) { + if (slotid == 0 || slotid > xhci->numslots || + !xhci->slots[slotid - 1].enabled) { DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); return; } diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index ab8e095b73f..20af2e7ebd7 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -124,6 +124,7 @@ virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uin virtio_iommu_set_page_size_mask(const char *name, uint64_t old, uint64_t new) "mr=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" +virtio_iommu_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" # virtio-mem.c virtio_mem_send_response(uint16_t type) "type=%" PRIu16 diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 4de8b6b3b03..8e581575c9b 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -146,6 +146,12 @@ static int vhost_kernel_set_vring_call(struct vhost_dev *dev, return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file); } +static int vhost_kernel_set_vring_err(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_ERR, file); +} + static int vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *dev, struct vhost_vring_state *s) { @@ -309,6 +315,7 @@ const VhostOps kernel_ops = { .vhost_get_vring_base = vhost_kernel_get_vring_base, .vhost_set_vring_kick = vhost_kernel_set_vring_kick, .vhost_set_vring_call = vhost_kernel_set_vring_call, + .vhost_set_vring_err = vhost_kernel_set_vring_err, .vhost_set_vring_busyloop_timeout = vhost_kernel_set_vring_busyloop_timeout, .vhost_set_features = vhost_kernel_set_features, diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index 56c96ebd13a..e4956728dd7 100644 --- a/hw/virtio/vhost-shadow-virtqueue.c +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -122,17 +122,35 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, return true; } -static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, - const struct iovec *iovec, size_t num, - bool more_descs, bool write) +/** + * Write descriptors to SVQ vring + * + * @svq: The shadow virtqueue + * @sg: Cache for hwaddr + * @iovec: The iovec from the guest + * @num: iovec length + * @more_descs: True if more descriptors come in the chain + * @write: True if they are writeable descriptors + * + * Return true if success, false otherwise and print error. + */ +static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, + const struct iovec *iovec, size_t num, + bool more_descs, bool write) { uint16_t i = svq->free_head, last = svq->free_head; unsigned n; uint16_t flags = write ? cpu_to_le16(VRING_DESC_F_WRITE) : 0; vring_desc_t *descs = svq->vring.desc; + bool ok; if (num == 0) { - return; + return true; + } + + ok = vhost_svq_translate_addr(svq, sg, iovec, num); + if (unlikely(!ok)) { + return false; } for (n = 0; n < num; n++) { @@ -150,40 +168,39 @@ static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, } svq->free_head = le16_to_cpu(svq->desc_next[last]); + return true; } static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, - VirtQueueElement *elem, unsigned *head) + const struct iovec *out_sg, size_t out_num, + const struct iovec *in_sg, size_t in_num, + unsigned *head) { unsigned avail_idx; vring_avail_t *avail = svq->vring.avail; bool ok; - g_autofree hwaddr *sgs = g_new(hwaddr, MAX(elem->out_num, elem->in_num)); + g_autofree hwaddr *sgs = g_new(hwaddr, MAX(out_num, in_num)); *head = svq->free_head; /* We need some descriptors here */ - if (unlikely(!elem->out_num && !elem->in_num)) { + if (unlikely(!out_num && !in_num)) { qemu_log_mask(LOG_GUEST_ERROR, "Guest provided element with no descriptors"); return false; } - ok = vhost_svq_translate_addr(svq, sgs, elem->out_sg, elem->out_num); + ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, in_num > 0, + false); if (unlikely(!ok)) { return false; } - vhost_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num, - elem->in_num > 0, false); - - ok = vhost_svq_translate_addr(svq, sgs, elem->in_sg, elem->in_num); + ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, false, true); if (unlikely(!ok)) { return false; } - vhost_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false, true); - /* * Put the entry in the available array (but don't update avail->idx until * they do sync). @@ -199,38 +216,58 @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, return true; } +static void vhost_svq_kick(VhostShadowVirtqueue *svq) +{ + /* + * We need to expose the available array entries before checking the used + * flags + */ + smp_mb(); + if (svq->vring.used->flags & VRING_USED_F_NO_NOTIFY) { + return; + } + + event_notifier_set(&svq->hdev_kick); +} + /** * Add an element to a SVQ. * * The caller must check that there is enough slots for the new element. It - * takes ownership of the element: In case of failure, it is free and the SVQ - * is considered broken. + * takes ownership of the element: In case of failure not ENOSPC, it is free. + * + * Return -EINVAL if element is invalid, -ENOSPC if dev queue is full */ -static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem) +int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, + size_t out_num, const struct iovec *in_sg, size_t in_num, + VirtQueueElement *elem) { unsigned qemu_head; - bool ok = vhost_svq_add_split(svq, elem, &qemu_head); + unsigned ndescs = in_num + out_num; + bool ok; + + if (unlikely(ndescs > vhost_svq_available_slots(svq))) { + return -ENOSPC; + } + + ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num, &qemu_head); if (unlikely(!ok)) { g_free(elem); - return false; + return -EINVAL; } - svq->ring_id_maps[qemu_head] = elem; - return true; + svq->desc_state[qemu_head].elem = elem; + svq->desc_state[qemu_head].ndescs = ndescs; + vhost_svq_kick(svq); + return 0; } -static void vhost_svq_kick(VhostShadowVirtqueue *svq) +/* Convenience wrapper to add a guest's element to SVQ */ +static int vhost_svq_add_element(VhostShadowVirtqueue *svq, + VirtQueueElement *elem) { - /* - * We need to expose the available array entries before checking the used - * flags - */ - smp_mb(); - if (svq->vring.used->flags & VRING_USED_F_NO_NOTIFY) { - return; - } - - event_notifier_set(&svq->hdev_kick); + return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->in_sg, + elem->in_num, elem); } /** @@ -257,7 +294,7 @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq) while (true) { VirtQueueElement *elem; - bool ok; + int r; if (svq->next_guest_avail_elem) { elem = g_steal_pointer(&svq->next_guest_avail_elem); @@ -269,28 +306,30 @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq) break; } - if (elem->out_num + elem->in_num > vhost_svq_available_slots(svq)) { - /* - * This condition is possible since a contiguous buffer in GPA - * does not imply a contiguous buffer in qemu's VA - * scatter-gather segments. If that happens, the buffer exposed - * to the device needs to be a chain of descriptors at this - * moment. - * - * SVQ cannot hold more available buffers if we are here: - * queue the current guest descriptor and ignore further kicks - * until some elements are used. - */ - svq->next_guest_avail_elem = elem; - return; + if (svq->ops) { + r = svq->ops->avail_handler(svq, elem, svq->ops_opaque); + } else { + r = vhost_svq_add_element(svq, elem); } - - ok = vhost_svq_add(svq, elem); - if (unlikely(!ok)) { - /* VQ is broken, just return and ignore any other kicks */ + if (unlikely(r != 0)) { + if (r == -ENOSPC) { + /* + * This condition is possible since a contiguous buffer in + * GPA does not imply a contiguous buffer in qemu's VA + * scatter-gather segments. If that happens, the buffer + * exposed to the device needs to be a chain of descriptors + * at this moment. + * + * SVQ cannot hold more available buffers if we are here: + * queue the current guest descriptor and ignore kicks + * until some elements are used. + */ + svq->next_guest_avail_elem = elem; + } + + /* VQ is full or broken, just return and ignore kicks */ return; } - vhost_svq_kick(svq); } virtio_queue_set_notification(svq->vq, true); @@ -311,11 +350,12 @@ static void vhost_handle_guest_kick_notifier(EventNotifier *n) static bool vhost_svq_more_used(VhostShadowVirtqueue *svq) { + uint16_t *used_idx = &svq->vring.used->idx; if (svq->last_used_idx != svq->shadow_used_idx) { return true; } - svq->shadow_used_idx = cpu_to_le16(svq->vring.used->idx); + svq->shadow_used_idx = cpu_to_le16(*(volatile uint16_t *)used_idx); return svq->last_used_idx != svq->shadow_used_idx; } @@ -376,21 +416,36 @@ static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq, return NULL; } - if (unlikely(!svq->ring_id_maps[used_elem.id])) { + if (unlikely(!svq->desc_state[used_elem.id].elem)) { qemu_log_mask(LOG_GUEST_ERROR, "Device %s says index %u is used, but it was not available", svq->vdev->name, used_elem.id); return NULL; } - num = svq->ring_id_maps[used_elem.id]->in_num + - svq->ring_id_maps[used_elem.id]->out_num; + num = svq->desc_state[used_elem.id].ndescs; last_used_chain = vhost_svq_last_desc_of_chain(svq, num, used_elem.id); svq->desc_next[last_used_chain] = svq->free_head; svq->free_head = used_elem.id; *len = used_elem.len; - return g_steal_pointer(&svq->ring_id_maps[used_elem.id]); + return g_steal_pointer(&svq->desc_state[used_elem.id].elem); +} + +/** + * Push an element to SVQ, returning it to the guest. + */ +void vhost_svq_push_elem(VhostShadowVirtqueue *svq, + const VirtQueueElement *elem, uint32_t len) +{ + virtqueue_push(svq->vq, elem, len); + if (svq->next_guest_avail_elem) { + /* + * Avail ring was full when vhost_svq_flush was called, so it's a + * good moment to make more descriptors available if possible. + */ + vhost_handle_guest_kick(svq); + } } static void vhost_svq_flush(VhostShadowVirtqueue *svq, @@ -434,6 +489,33 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq, } while (!vhost_svq_enable_notification(svq)); } +/** + * Poll the SVQ for one device used buffer. + * + * This function race with main event loop SVQ polling, so extra + * synchronization is needed. + * + * Return the length written by the device. + */ +size_t vhost_svq_poll(VhostShadowVirtqueue *svq) +{ + int64_t start_us = g_get_monotonic_time(); + do { + uint32_t len; + VirtQueueElement *elem = vhost_svq_get_buf(svq, &len); + if (elem) { + return len; + } + + if (unlikely(g_get_monotonic_time() - start_us > 10e6)) { + return 0; + } + + /* Make sure we read new used_idx */ + smp_rmb(); + } while (true); +} + /** * Forward used buffers. * @@ -560,7 +642,7 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, memset(svq->vring.desc, 0, driver_size); svq->vring.used = qemu_memalign(qemu_real_host_page_size(), device_size); memset(svq->vring.used, 0, device_size); - svq->ring_id_maps = g_new0(VirtQueueElement *, svq->vring.num); + svq->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); @@ -585,7 +667,7 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq) for (unsigned i = 0; i < svq->vring.num; ++i) { g_autofree VirtQueueElement *elem = NULL; - elem = g_steal_pointer(&svq->ring_id_maps[i]); + elem = g_steal_pointer(&svq->desc_state[i].elem); if (elem) { virtqueue_detach_element(svq->vq, elem, 0); } @@ -597,7 +679,7 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq) } svq->vq = NULL; g_free(svq->desc_next); - g_free(svq->ring_id_maps); + g_free(svq->desc_state); qemu_vfree(svq->vring.desc); qemu_vfree(svq->vring.used); } @@ -607,12 +689,16 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq) * shadow methods and file descriptors. * * @iova_tree: Tree to perform descriptors translations + * @ops: SVQ owner callbacks + * @ops_opaque: ops opaque pointer * * Returns the new virtqueue or NULL. * * In case of error, reason is reported through error_report. */ -VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree) +VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree, + const VhostShadowVirtqueueOps *ops, + void *ops_opaque) { g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1); int r; @@ -634,6 +720,8 @@ VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree) event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND); event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call); svq->iova_tree = iova_tree; + svq->ops = ops; + svq->ops_opaque = ops_opaque; return g_steal_pointer(&svq); err_init_hdev_call: diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h index c132c994e93..d04c34a5896 100644 --- a/hw/virtio/vhost-shadow-virtqueue.h +++ b/hw/virtio/vhost-shadow-virtqueue.h @@ -15,6 +15,37 @@ #include "standard-headers/linux/vhost_types.h" #include "hw/virtio/vhost-iova-tree.h" +typedef struct SVQDescState { + VirtQueueElement *elem; + + /* + * Number of descriptors exposed to the device. May or may not match + * guest's + */ + unsigned int ndescs; +} SVQDescState; + +typedef struct VhostShadowVirtqueue VhostShadowVirtqueue; + +/** + * Callback to handle an avail buffer. + * + * @svq: Shadow virtqueue + * @elem: Element placed in the queue by the guest + * @vq_callback_opaque: Opaque + * + * Returns 0 if the vq is running as expected. + * + * Note that ownership of elem is transferred to the callback. + */ +typedef int (*VirtQueueAvailCallback)(VhostShadowVirtqueue *svq, + VirtQueueElement *elem, + void *vq_callback_opaque); + +typedef struct VhostShadowVirtqueueOps { + VirtQueueAvailCallback avail_handler; +} VhostShadowVirtqueueOps; + /* Shadow virtqueue to relay notifications */ typedef struct VhostShadowVirtqueue { /* Shadow vring */ @@ -47,8 +78,8 @@ typedef struct VhostShadowVirtqueue { /* IOVA mapping */ VhostIOVATree *iova_tree; - /* Map for use the guest's descriptors */ - VirtQueueElement **ring_id_maps; + /* SVQ vring descriptors state */ + SVQDescState *desc_state; /* Next VirtQueue element that guest made available */ VirtQueueElement *next_guest_avail_elem; @@ -59,6 +90,12 @@ typedef struct VhostShadowVirtqueue { */ uint16_t *desc_next; + /* Caller callbacks */ + const VhostShadowVirtqueueOps *ops; + + /* Caller callbacks opaque */ + void *ops_opaque; + /* Next head to expose to the device */ uint16_t shadow_avail_idx; @@ -74,6 +111,13 @@ typedef struct VhostShadowVirtqueue { bool vhost_svq_valid_features(uint64_t features, Error **errp); +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); + void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd); void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd); void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq, @@ -85,7 +129,9 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, VirtQueue *vq); void vhost_svq_stop(VhostShadowVirtqueue *svq); -VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree); +VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree, + const VhostShadowVirtqueueOps *ops, + void *ops_opaque); void vhost_svq_free(gpointer vq); G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free); diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 05941782240..75b8df21a48 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1313,6 +1313,11 @@ static int vhost_user_set_vring_call(struct vhost_dev *dev, return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file); } +static int vhost_user_set_vring_err(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + 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) { @@ -1525,7 +1530,7 @@ static VhostUserHostNotifier *fetch_or_create_notifier(VhostUserState *u, { VhostUserHostNotifier *n = NULL; if (idx >= u->notifiers->len) { - g_ptr_array_set_size(u->notifiers, idx); + g_ptr_array_set_size(u->notifiers, idx + 1); } n = g_ptr_array_index(u->notifiers, idx); @@ -2616,6 +2621,7 @@ const VhostOps user_ops = { .vhost_get_vring_base = vhost_user_get_vring_base, .vhost_set_vring_kick = vhost_user_set_vring_kick, .vhost_set_vring_call = vhost_user_set_vring_call, + .vhost_set_vring_err = vhost_user_set_vring_err, .vhost_set_features = vhost_user_set_features, .vhost_get_features = vhost_user_get_features, .vhost_set_owner = vhost_user_set_owner, diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 66f054a12c2..291cd190540 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -20,6 +20,7 @@ #include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/vhost-vdpa.h" #include "exec/address-spaces.h" +#include "migration/blocker.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "cpu.h" @@ -71,8 +72,8 @@ static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, return false; } -static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, - void *vaddr, bool readonly) +int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, + void *vaddr, bool readonly) { struct vhost_msg_v2 msg = {}; int fd = v->device_fd; @@ -97,8 +98,7 @@ static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, return ret; } -static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, - hwaddr size) +int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, hwaddr size) { struct vhost_msg_v2 msg = {}; int fd = v->device_fd; @@ -418,8 +418,10 @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v, shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free); for (unsigned n = 0; n < hdev->nvqs; ++n) { - g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(v->iova_tree); + g_autoptr(VhostShadowVirtqueue) svq; + svq = vhost_svq_new(v->iova_tree, v->shadow_vq_ops, + v->shadow_vq_ops_opaque); if (unlikely(!svq)) { error_setg(errp, "Cannot create svq %u", n); return -1; @@ -1021,6 +1023,13 @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) return true; } + if (v->migration_blocker) { + int r = migrate_add_blocker(v->migration_blocker, &err); + if (unlikely(r < 0)) { + return false; + } + } + for (i = 0; i < v->shadow_vqs->len; ++i) { VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i); VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); @@ -1063,6 +1072,10 @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) vhost_svq_stop(svq); } + if (v->migration_blocker) { + migrate_del_blocker(v->migration_blocker); + } + return false; } @@ -1082,6 +1095,9 @@ static bool vhost_vdpa_svqs_stop(struct vhost_dev *dev) } } + if (v->migration_blocker) { + migrate_del_blocker(v->migration_blocker); + } return true; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index dd3263df56d..0827d631c09 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -886,6 +886,10 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) err_vq: for (; i >= 0; --i) { idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i); + addr = virtio_queue_get_desc_addr(dev->vdev, idx); + if (!addr) { + continue; + } vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, dev->log_enabled); } @@ -1274,6 +1278,19 @@ static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, return 0; } +static void vhost_virtqueue_error_notifier(EventNotifier *n) +{ + struct vhost_virtqueue *vq = container_of(n, struct vhost_virtqueue, + error_notifier); + struct vhost_dev *dev = vq->dev; + int index = vq - dev->vqs; + + if (event_notifier_test_and_clear(n) && dev->vdev) { + VHOST_OPS_DEBUG(-EINVAL, "vhost vring error in virtqueue %d", + dev->vq_index + index); + } +} + static int vhost_virtqueue_init(struct vhost_dev *dev, struct vhost_virtqueue *vq, int n) { @@ -1295,7 +1312,27 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, vq->dev = dev; + if (dev->vhost_ops->vhost_set_vring_err) { + r = event_notifier_init(&vq->error_notifier, 0); + if (r < 0) { + goto fail_call; + } + + file.fd = event_notifier_get_fd(&vq->error_notifier); + r = dev->vhost_ops->vhost_set_vring_err(dev, &file); + if (r) { + VHOST_OPS_DEBUG(r, "vhost_set_vring_err failed"); + goto fail_err; + } + + event_notifier_set_handler(&vq->error_notifier, + vhost_virtqueue_error_notifier); + } + return 0; + +fail_err: + event_notifier_cleanup(&vq->error_notifier); fail_call: event_notifier_cleanup(&vq->masked_notifier); return r; @@ -1304,6 +1341,10 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) { event_notifier_cleanup(&vq->masked_notifier); + if (vq->dev->vhost_ops->vhost_set_vring_err) { + event_notifier_set_handler(&vq->error_notifier, NULL); + event_notifier_cleanup(&vq->error_notifier); + } } int vhost_dev_init(struct vhost_dev *hdev, void *opaque, diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d7ec023adf2..896feb37a1c 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -104,6 +104,7 @@ void virtio_bus_reset(VirtioBusState *bus) VirtIODevice *vdev = virtio_bus_get_device(bus); DPRINTF("%s: reset device.\n", BUS(bus)->name); + virtio_bus_stop_ioeventfd(bus); if (vdev != NULL) { virtio_reset(vdev); } diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index c3829e74983..c1243c3f93c 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -83,7 +83,8 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, struct iovec *iov, unsigned int out_num) { VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); - CryptoDevBackendSymSessionInfo info; + CryptoDevBackendSessionInfo info; + CryptoDevBackendSymSessionInfo *sym_info; int64_t session_id; int queue_index; uint32_t op_type; @@ -92,11 +93,13 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, memset(&info, 0, sizeof(info)); op_type = ldl_le_p(&sess_req->op_type); - info.op_type = op_type; info.op_code = opcode; + sym_info = &info.u.sym_sess_info; + sym_info->op_type = op_type; + if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { - ret = virtio_crypto_cipher_session_helper(vdev, &info, + ret = virtio_crypto_cipher_session_helper(vdev, sym_info, &sess_req->u.cipher.para, &iov, &out_num); if (ret < 0) { @@ -105,47 +108,47 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { size_t s; /* cipher part */ - ret = virtio_crypto_cipher_session_helper(vdev, &info, + ret = virtio_crypto_cipher_session_helper(vdev, sym_info, &sess_req->u.chain.para.cipher_param, &iov, &out_num); if (ret < 0) { goto err; } /* hash part */ - info.alg_chain_order = ldl_le_p( + sym_info->alg_chain_order = ldl_le_p( &sess_req->u.chain.para.alg_chain_order); - info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); - info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); - if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { - info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); - info.auth_key_len = ldl_le_p( + sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); + sym_info->hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); + if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { + sym_info->hash_alg = + ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); + sym_info->auth_key_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.auth_key_len); - info.hash_result_len = ldl_le_p( + sym_info->hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.hash_result_len); - if (info.auth_key_len > vcrypto->conf.max_auth_key_len) { + if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) { error_report("virtio-crypto length of auth key is too big: %u", - info.auth_key_len); + sym_info->auth_key_len); ret = -VIRTIO_CRYPTO_ERR; goto err; } /* get auth key */ - if (info.auth_key_len > 0) { - DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len); - info.auth_key = g_malloc(info.auth_key_len); - s = iov_to_buf(iov, out_num, 0, info.auth_key, - info.auth_key_len); - if (unlikely(s != info.auth_key_len)) { + if (sym_info->auth_key_len > 0) { + sym_info->auth_key = g_malloc(sym_info->auth_key_len); + s = iov_to_buf(iov, out_num, 0, sym_info->auth_key, + sym_info->auth_key_len); + if (unlikely(s != sym_info->auth_key_len)) { virtio_error(vdev, "virtio-crypto authenticated key incorrect"); ret = -EFAULT; goto err; } - iov_discard_front(&iov, &out_num, info.auth_key_len); + iov_discard_front(&iov, &out_num, sym_info->auth_key_len); } - } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { - info.hash_alg = ldl_le_p( + } else if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { + sym_info->hash_alg = ldl_le_p( &sess_req->u.chain.para.u.hash_param.algo); - info.hash_result_len = ldl_le_p( + sym_info->hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.hash_param.hash_result_len); } else { /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */ @@ -161,13 +164,10 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, } queue_index = virtio_crypto_vq2q(queue_id); - session_id = cryptodev_backend_sym_create_session( + session_id = cryptodev_backend_create_session( vcrypto->cryptodev, &info, queue_index, &local_err); if (session_id >= 0) { - DPRINTF("create session_id=%" PRIu64 " successfully\n", - session_id); - ret = session_id; } else { if (local_err) { @@ -177,11 +177,78 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, } err: - g_free(info.cipher_key); - g_free(info.auth_key); + g_free(sym_info->cipher_key); + g_free(sym_info->auth_key); return ret; } +static int64_t +virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, + struct virtio_crypto_akcipher_create_session_req *sess_req, + uint32_t queue_id, uint32_t opcode, + struct iovec *iov, unsigned int out_num) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + CryptoDevBackendSessionInfo info = {0}; + CryptoDevBackendAsymSessionInfo *asym_info; + int64_t session_id; + int queue_index; + uint32_t algo, keytype, keylen; + g_autofree uint8_t *key = NULL; + Error *local_err = NULL; + + algo = ldl_le_p(&sess_req->para.algo); + keytype = ldl_le_p(&sess_req->para.keytype); + keylen = ldl_le_p(&sess_req->para.keylen); + + if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC) + && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) { + error_report("unsupported asym keytype: %d", keytype); + return -VIRTIO_CRYPTO_NOTSUPP; + } + + if (keylen) { + key = g_malloc(keylen); + if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) { + virtio_error(vdev, "virtio-crypto asym key incorrect"); + return -EFAULT; + } + iov_discard_front(&iov, &out_num, keylen); + } + + info.op_code = opcode; + asym_info = &info.u.asym_sess_info; + asym_info->algo = algo; + asym_info->keytype = keytype; + asym_info->keylen = keylen; + asym_info->key = key; + switch (asym_info->algo) { + case VIRTIO_CRYPTO_AKCIPHER_RSA: + asym_info->u.rsa.padding_algo = + ldl_le_p(&sess_req->para.u.rsa.padding_algo); + asym_info->u.rsa.hash_algo = + ldl_le_p(&sess_req->para.u.rsa.hash_algo); + break; + + /* TODO DSA&ECDSA handling */ + + default: + return -VIRTIO_CRYPTO_ERR; + } + + queue_index = virtio_crypto_vq2q(queue_id); + session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info, + queue_index, &local_err); + if (session_id < 0) { + if (local_err) { + error_report_err(local_err); + } + return -VIRTIO_CRYPTO_ERR; + } + + return session_id; +} + static uint8_t virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto, struct virtio_crypto_destroy_session_req *close_sess_req, @@ -195,7 +262,7 @@ virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto, session_id = ldq_le_p(&close_sess_req->session_id); DPRINTF("close session, id=%" PRIu64 "\n", session_id); - ret = cryptodev_backend_sym_close_session( + ret = cryptodev_backend_close_session( vcrypto->cryptodev, session_id, queue_id, &local_err); if (ret == 0) { status = VIRTIO_CRYPTO_OK; @@ -260,13 +327,22 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) opcode = ldl_le_p(&ctrl.header.opcode); queue_id = ldl_le_p(&ctrl.header.queue_id); + memset(&input, 0, sizeof(input)); switch (opcode) { case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: - memset(&input, 0, sizeof(input)); session_id = virtio_crypto_create_sym_session(vcrypto, &ctrl.u.sym_create_session, queue_id, opcode, out_iov, out_num); + goto check_session; + + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + session_id = virtio_crypto_create_asym_session(vcrypto, + &ctrl.u.akcipher_create_session, + queue_id, opcode, + out_iov, out_num); + +check_session: /* Serious errors, need to reset virtio crypto device */ if (session_id == -EFAULT) { virtqueue_detach_element(vq, elem, 0); @@ -290,10 +366,12 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) virtqueue_push(vq, elem, sizeof(input)); virtio_notify(vdev, vq); break; + case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION: case VIRTIO_CRYPTO_HASH_DESTROY_SESSION: case VIRTIO_CRYPTO_MAC_DESTROY_SESSION: case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION: + case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION: status = virtio_crypto_handle_close_session(vcrypto, &ctrl.u.destroy_session, queue_id); /* The status only occupy one byte, we can directly use it */ @@ -311,7 +389,6 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) case VIRTIO_CRYPTO_AEAD_CREATE_SESSION: default: error_report("virtio-crypto unsupported ctrl opcode: %d", opcode); - memset(&input, 0, sizeof(input)); stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); if (unlikely(s != sizeof(input))) { @@ -339,28 +416,39 @@ static void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq, req->in_num = 0; req->in_len = 0; req->flags = CRYPTODEV_BACKEND_ALG__MAX; - req->u.sym_op_info = NULL; + memset(&req->op_info, 0x00, sizeof(req->op_info)); } static void virtio_crypto_free_request(VirtIOCryptoReq *req) { - if (req) { - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { - size_t max_len; - CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info; - - max_len = op_info->iv_len + - op_info->aad_len + - op_info->src_len + - op_info->dst_len + - op_info->digest_result_len; - - /* Zeroize and free request data structure */ - memset(op_info, 0, sizeof(*op_info) + max_len); + if (!req) { + return; + } + + if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { + size_t max_len; + CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info; + + max_len = op_info->iv_len + + op_info->aad_len + + op_info->src_len + + op_info->dst_len + + op_info->digest_result_len; + + /* Zeroize and free request data structure */ + memset(op_info, 0, sizeof(*op_info) + max_len); + g_free(op_info); + } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) { + CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info; + if (op_info) { + g_free(op_info->src); + g_free(op_info->dst); + memset(op_info, 0, sizeof(*op_info)); g_free(op_info); } - g_free(req); } + + g_free(req); } static void @@ -397,6 +485,35 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, } } +static void +virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev, + VirtIOCryptoReq *req, int32_t status, + CryptoDevBackendAsymOpInfo *asym_op_info) +{ + size_t s, len; + + if (status != VIRTIO_CRYPTO_OK) { + return; + } + + len = asym_op_info->dst_len; + if (!len) { + return; + } + + s = iov_from_buf(req->in_iov, req->in_num, 0, asym_op_info->dst, len); + if (s != len) { + virtio_error(vdev, "virtio-crypto asym dest data incorrect"); + return; + } + + iov_discard_front(&req->in_iov, &req->in_num, len); + + /* For akcipher, dst_len may be changed after operation */ + req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len; +} + + static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status) { VirtIOCrypto *vcrypto = req->vcrypto; @@ -404,7 +521,10 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status) if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { virtio_crypto_sym_input_data_helper(vdev, req, status, - req->u.sym_op_info); + req->op_info.u.sym_op_info); + } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) { + virtio_crypto_akcipher_input_data_helper(vdev, req, status, + req->op_info.u.asym_op_info); } stb_p(&req->in->status, status); virtqueue_push(req->vq, &req->elem, req->in_len); @@ -543,41 +663,100 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, static int virtio_crypto_handle_sym_req(VirtIOCrypto *vcrypto, struct virtio_crypto_sym_data_req *req, - CryptoDevBackendSymOpInfo **sym_op_info, + CryptoDevBackendOpInfo *op_info, struct iovec *iov, unsigned int out_num) { VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + CryptoDevBackendSymOpInfo *sym_op_info; uint32_t op_type; - CryptoDevBackendSymOpInfo *op_info; op_type = ldl_le_p(&req->op_type); - if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { - op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para, + sym_op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para, NULL, iov, out_num); - if (!op_info) { + if (!sym_op_info) { return -EFAULT; } - op_info->op_type = op_type; } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { - op_info = virtio_crypto_sym_op_helper(vdev, NULL, + sym_op_info = virtio_crypto_sym_op_helper(vdev, NULL, &req->u.chain.para, iov, out_num); - if (!op_info) { + if (!sym_op_info) { return -EFAULT; } - op_info->op_type = op_type; } else { /* VIRTIO_CRYPTO_SYM_OP_NONE */ error_report("virtio-crypto unsupported cipher type"); return -VIRTIO_CRYPTO_NOTSUPP; } - *sym_op_info = op_info; + sym_op_info->op_type = op_type; + op_info->u.sym_op_info = sym_op_info; return 0; } +static int +virtio_crypto_handle_asym_req(VirtIOCrypto *vcrypto, + struct virtio_crypto_akcipher_data_req *req, + CryptoDevBackendOpInfo *op_info, + struct iovec *iov, unsigned int out_num) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + CryptoDevBackendAsymOpInfo *asym_op_info; + uint32_t src_len; + uint32_t dst_len; + uint32_t len; + uint8_t *src = NULL; + uint8_t *dst = NULL; + + asym_op_info = g_malloc0(sizeof(CryptoDevBackendAsymOpInfo)); + src_len = ldl_le_p(&req->para.src_data_len); + dst_len = ldl_le_p(&req->para.dst_data_len); + + if (src_len > 0) { + src = g_malloc0(src_len); + len = iov_to_buf(iov, out_num, 0, src, src_len); + if (unlikely(len != src_len)) { + virtio_error(vdev, "virtio-crypto asym src data incorrect" + "expected %u, actual %u", src_len, len); + goto err; + } + + iov_discard_front(&iov, &out_num, src_len); + } + + if (dst_len > 0) { + dst = g_malloc0(dst_len); + + if (op_info->op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) { + len = iov_to_buf(iov, out_num, 0, dst, dst_len); + if (unlikely(len != dst_len)) { + virtio_error(vdev, "virtio-crypto asym dst data incorrect" + "expected %u, actual %u", dst_len, len); + goto err; + } + + iov_discard_front(&iov, &out_num, dst_len); + } + } + + asym_op_info->src_len = src_len; + asym_op_info->dst_len = dst_len; + asym_op_info->src = src; + asym_op_info->dst = dst; + op_info->u.asym_op_info = asym_op_info; + + return 0; + + err: + g_free(asym_op_info); + g_free(src); + g_free(dst); + + return -EFAULT; +} + static int virtio_crypto_handle_request(VirtIOCryptoReq *request) { @@ -595,8 +774,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) unsigned out_num; uint32_t opcode; uint8_t status = VIRTIO_CRYPTO_ERR; - uint64_t session_id; - CryptoDevBackendSymOpInfo *sym_op_info = NULL; + CryptoDevBackendOpInfo *op_info = &request->op_info; Error *local_err = NULL; if (elem->out_num < 1 || elem->in_num < 1) { @@ -639,15 +817,28 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) request->in_iov = in_iov; opcode = ldl_le_p(&req.header.opcode); - session_id = ldq_le_p(&req.header.session_id); + op_info->session_id = ldq_le_p(&req.header.session_id); + op_info->op_code = opcode; switch (opcode) { case VIRTIO_CRYPTO_CIPHER_ENCRYPT: case VIRTIO_CRYPTO_CIPHER_DECRYPT: + op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_SYM; ret = virtio_crypto_handle_sym_req(vcrypto, - &req.u.sym_req, - &sym_op_info, + &req.u.sym_req, op_info, + out_iov, out_num); + goto check_result; + + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: + case VIRTIO_CRYPTO_AKCIPHER_SIGN: + case VIRTIO_CRYPTO_AKCIPHER_VERIFY: + op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_ASYM; + ret = virtio_crypto_handle_asym_req(vcrypto, + &req.u.akcipher_req, op_info, out_iov, out_num); + +check_result: /* Serious errors, need to reset virtio crypto device */ if (ret == -EFAULT) { return -1; @@ -655,11 +846,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); virtio_crypto_free_request(request); } else { - sym_op_info->session_id = session_id; /* Set request's parameter */ - request->flags = CRYPTODEV_BACKEND_ALG_SYM; - request->u.sym_op_info = sym_op_info; ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev, request, queue_index, &local_err); if (ret < 0) { @@ -674,6 +862,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) virtio_crypto_free_request(request); } break; + case VIRTIO_CRYPTO_HASH: case VIRTIO_CRYPTO_MAC: case VIRTIO_CRYPTO_AEAD_ENCRYPT: @@ -779,6 +968,7 @@ static void virtio_crypto_init_config(VirtIODevice *vdev) vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l; vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h; vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo; + vcrypto->conf.akcipher_algo = vcrypto->conf.cryptodev->conf.akcipher_algo; vcrypto->conf.max_cipher_key_len = vcrypto->conf.cryptodev->conf.max_cipher_key_len; vcrypto->conf.max_auth_key_len = @@ -891,6 +1081,7 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config) stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len); stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len); stq_le_p(&crypto_cfg.max_size, c->conf.max_size); + stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo); memcpy(config, &crypto_cfg, c->config_size); } diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 2597e166f93..281152d338f 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -69,6 +69,77 @@ static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev) return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); } +static bool virtio_iommu_device_bypassed(IOMMUDevice *sdev) +{ + uint32_t sid; + bool bypassed; + VirtIOIOMMU *s = sdev->viommu; + VirtIOIOMMUEndpoint *ep; + + sid = virtio_iommu_get_bdf(sdev); + + qemu_rec_mutex_lock(&s->mutex); + /* need to check bypass before system reset */ + if (!s->endpoints) { + bypassed = s->config.bypass; + goto unlock; + } + + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid)); + if (!ep || !ep->domain) { + bypassed = s->config.bypass; + } else { + bypassed = ep->domain->bypass; + } + +unlock: + qemu_rec_mutex_unlock(&s->mutex); + return bypassed; +} + +/* Return whether the device is using IOMMU translation. */ +static bool virtio_iommu_switch_address_space(IOMMUDevice *sdev) +{ + bool use_remapping; + + assert(sdev); + + use_remapping = !virtio_iommu_device_bypassed(sdev); + + trace_virtio_iommu_switch_address_space(pci_bus_num(sdev->bus), + PCI_SLOT(sdev->devfn), + PCI_FUNC(sdev->devfn), + use_remapping); + + /* Turn off first then on the other */ + if (use_remapping) { + memory_region_set_enabled(&sdev->bypass_mr, false); + memory_region_set_enabled(MEMORY_REGION(&sdev->iommu_mr), true); + } else { + memory_region_set_enabled(MEMORY_REGION(&sdev->iommu_mr), false); + memory_region_set_enabled(&sdev->bypass_mr, true); + } + + return use_remapping; +} + +static void virtio_iommu_switch_address_space_all(VirtIOIOMMU *s) +{ + GHashTableIter iter; + IOMMUPciBus *iommu_pci_bus; + int i; + + g_hash_table_iter_init(&iter, s->as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&iommu_pci_bus)) { + for (i = 0; i < PCI_DEVFN_MAX; i++) { + if (!iommu_pci_bus->pbdev[i]) { + continue; + } + virtio_iommu_switch_address_space(iommu_pci_bus->pbdev[i]); + } + } +} + /** * The bus number is used for lookup when SID based operations occur. * In that case we lazily populate the IOMMUPciBus array from the bus hash @@ -213,6 +284,7 @@ static gboolean virtio_iommu_notify_map_cb(gpointer key, gpointer value, static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) { VirtIOIOMMUDomain *domain = ep->domain; + IOMMUDevice *sdev = container_of(ep->iommu_mr, IOMMUDevice, iommu_mr); if (!ep->domain) { return; @@ -221,6 +293,7 @@ static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) ep->iommu_mr); QLIST_REMOVE(ep, next); ep->domain = NULL; + virtio_iommu_switch_address_space(sdev); } static VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, @@ -323,12 +396,39 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, trace_virtio_iommu_init_iommu_mr(name); + memory_region_init(&sdev->root, OBJECT(s), name, UINT64_MAX); + address_space_init(&sdev->as, &sdev->root, TYPE_VIRTIO_IOMMU); + + /* + * Build the IOMMU disabled container with aliases to the + * shared MRs. Note that aliasing to a shared memory region + * could help the memory API to detect same FlatViews so we + * can have devices to share the same FlatView when in bypass + * mode. (either by not configuring virtio-iommu driver or with + * "iommu=pt"). It will greatly reduce the total number of + * FlatViews of the system hence VM runs faster. + */ + memory_region_init_alias(&sdev->bypass_mr, OBJECT(s), + "system", get_system_memory(), 0, + memory_region_size(get_system_memory())); + memory_region_init_iommu(&sdev->iommu_mr, sizeof(sdev->iommu_mr), TYPE_VIRTIO_IOMMU_MEMORY_REGION, OBJECT(s), name, UINT64_MAX); - address_space_init(&sdev->as, - MEMORY_REGION(&sdev->iommu_mr), TYPE_VIRTIO_IOMMU); + + /* + * Hook both the containers under the root container, we + * switch between iommu & bypass MRs by enable/disable + * corresponding sub-containers + */ + memory_region_add_subregion_overlap(&sdev->root, 0, + MEMORY_REGION(&sdev->iommu_mr), + 0); + memory_region_add_subregion_overlap(&sdev->root, 0, + &sdev->bypass_mr, 0); + + virtio_iommu_switch_address_space(sdev); g_free(name); } return &sdev->as; @@ -342,6 +442,7 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, uint32_t flags = le32_to_cpu(req->flags); VirtIOIOMMUDomain *domain; VirtIOIOMMUEndpoint *ep; + IOMMUDevice *sdev; trace_virtio_iommu_attach(domain_id, ep_id); @@ -375,6 +476,8 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next); ep->domain = domain; + sdev = container_of(ep->iommu_mr, IOMMUDevice, iommu_mr); + virtio_iommu_switch_address_space(sdev); /* Replay domain mappings on the associated memory region */ g_tree_foreach(domain->mappings, virtio_iommu_notify_map_cb, @@ -572,11 +675,10 @@ static int virtio_iommu_probe(VirtIOIOMMU *s, static int virtio_iommu_iov_to_req(struct iovec *iov, unsigned int iov_cnt, - void *req, size_t req_sz) + void *req, size_t payload_sz) { - size_t sz, payload_sz = req_sz - sizeof(struct virtio_iommu_req_tail); + size_t sz = iov_to_buf(iov, iov_cnt, 0, req, payload_sz); - sz = iov_to_buf(iov, iov_cnt, 0, req, payload_sz); if (unlikely(sz != payload_sz)) { return VIRTIO_IOMMU_S_INVAL; } @@ -589,7 +691,8 @@ static int virtio_iommu_handle_ ## __req(VirtIOIOMMU *s, \ unsigned int iov_cnt) \ { \ struct virtio_iommu_req_ ## __req req; \ - int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, sizeof(req)); \ + int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, \ + sizeof(req) - sizeof(struct virtio_iommu_req_tail));\ \ return ret ? ret : virtio_iommu_ ## __req(s, &req); \ } @@ -642,7 +745,7 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) tail.status = VIRTIO_IOMMU_S_DEVERR; goto out; } - qemu_mutex_lock(&s->mutex); + qemu_rec_mutex_lock(&s->mutex); switch (head.type) { case VIRTIO_IOMMU_T_ATTACH: tail.status = virtio_iommu_handle_attach(s, iov, iov_cnt); @@ -671,7 +774,7 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) default: tail.status = VIRTIO_IOMMU_S_UNSUPP; } - qemu_mutex_unlock(&s->mutex); + qemu_rec_mutex_unlock(&s->mutex); out: sz = iov_from_buf(elem->in_sg, elem->in_num, 0, @@ -759,9 +862,13 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, sid = virtio_iommu_get_bdf(sdev); trace_virtio_iommu_translate(mr->parent_obj.name, sid, addr, flag); - qemu_mutex_lock(&s->mutex); + qemu_rec_mutex_lock(&s->mutex); ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid)); + + if (bypass_allowed) + assert(ep && ep->domain && !ep->domain->bypass); + if (!ep) { if (!bypass_allowed) { error_report_once("%s sid=%d is not known!!", __func__, sid); @@ -843,7 +950,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, trace_virtio_iommu_translate_out(addr, entry.translated_addr, sid); unlock: - qemu_mutex_unlock(&s->mutex); + qemu_rec_mutex_unlock(&s->mutex); return entry; } @@ -887,6 +994,7 @@ static void virtio_iommu_set_config(VirtIODevice *vdev, return; } dev_config->bypass = in_config->bypass; + virtio_iommu_switch_address_space_all(dev); } trace_virtio_iommu_set_config(in_config->bypass); @@ -931,7 +1039,7 @@ static void virtio_iommu_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n) sid = virtio_iommu_get_bdf(sdev); - qemu_mutex_lock(&s->mutex); + qemu_rec_mutex_lock(&s->mutex); if (!s->endpoints) { goto unlock; @@ -945,7 +1053,7 @@ static void virtio_iommu_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n) g_tree_foreach(ep->domain->mappings, virtio_iommu_remap, mr); unlock: - qemu_mutex_unlock(&s->mutex); + qemu_rec_mutex_unlock(&s->mutex); } static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, @@ -1026,6 +1134,8 @@ static void virtio_iommu_system_reset(void *opaque) * system reset */ s->config.bypass = s->boot_bypass; + virtio_iommu_switch_address_space_all(s); + } static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) @@ -1041,6 +1151,11 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_iommu_handle_command); s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL); + /* + * config.bypass is needed to get initial address space early, such as + * in vfio realize + */ + s->config.bypass = s->boot_bypass; s->config.page_size_mask = TARGET_PAGE_MASK; s->config.input_range.end = UINT64_MAX; s->config.domain_range.end = UINT32_MAX; @@ -1056,7 +1171,7 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS_CONFIG); - qemu_mutex_init(&s->mutex); + qemu_rec_mutex_init(&s->mutex); s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free); @@ -1084,6 +1199,8 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) g_tree_destroy(s->endpoints); } + qemu_rec_mutex_destroy(&s->mutex); + virtio_delete_queue(s->req_vq); virtio_delete_queue(s->event_vq); virtio_cleanup(vdev); @@ -1205,6 +1322,14 @@ static int iommu_post_load(void *opaque, int version_id) VirtIOIOMMU *s = opaque; g_tree_foreach(s->domains, reconstruct_endpoints, s); + + /* + * Memory regions are dynamically turned on/off depending on + * 'config.bypass' and attached domain type if there is. After + * migration, we need to make sure the memory regions are + * still correct. + */ + virtio_iommu_switch_address_space_all(s); return 0; } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 688eccda94d..d240efef97f 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -72,12 +72,12 @@ static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy) { int i; - if (proxy->legacy) { - return; - } + virtio_bus_reset(&proxy->bus); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - proxy->vqs[i].enabled = 0; + if (!proxy->legacy) { + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + proxy->vqs[i].enabled = 0; + } } } @@ -376,7 +376,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, return; } if (value == 0) { - virtio_reset(vdev); + virtio_mmio_soft_reset(proxy); } else { virtio_queue_set_addr(vdev, vdev->queue_sel, value << proxy->guest_page_shift); @@ -432,7 +432,6 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, } if (vdev->status == 0) { - virtio_reset(vdev); virtio_mmio_soft_reset(proxy); } break; @@ -627,8 +626,8 @@ static void virtio_mmio_reset(DeviceState *d) VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); int i; - virtio_mmio_stop_ioeventfd(proxy); - virtio_bus_reset(&proxy->bus); + virtio_mmio_soft_reset(proxy); + proxy->host_features_sel = 0; proxy->guest_features_sel = 0; proxy->guest_page_shift = 0; @@ -637,7 +636,6 @@ static void virtio_mmio_reset(DeviceState *d) proxy->guest_features[0] = proxy->guest_features[1] = 0; for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - proxy->vqs[i].enabled = 0; proxy->vqs[i].num = 0; proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0; proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 0566ad7d003..45327f0b319 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1945,7 +1945,6 @@ static void virtio_pci_reset(DeviceState *qdev) PCIDevice *dev = PCI_DEVICE(qdev); int i; - virtio_pci_stop_ioeventfd(proxy); virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 054c403dea7..8974b5cf4c8 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -6,3 +6,4 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) +specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c')) diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c new file mode 100644 index 00000000000..55ff1f03c1d --- /dev/null +++ b/hw/watchdog/spapr_watchdog.c @@ -0,0 +1,274 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "target/ppc/cpu.h" +#include "migration/vmstate.h" +#include "trace.h" + +#include "hw/ppc/spapr.h" + +#define FIELD_BE(reg, field, start, len) \ + FIELD(reg, field, 64 - (start + len), len) + +/* + * Bits 47: "leaveOtherWatchdogsRunningOnTimeout", specified on + * the "Start watchdog" operation, + * 0 - stop out-standing watchdogs on timeout, + * 1 - leave outstanding watchdogs running on timeout + */ +FIELD_BE(PSERIES_WDTF, LEAVE_OTHER, 47, 1) + +/* Bits 48-55: "operation" */ +FIELD_BE(PSERIES_WDTF, OP, 48, 8) +#define PSERIES_WDTF_OP_START 0x1 +#define PSERIES_WDTF_OP_STOP 0x2 +#define PSERIES_WDTF_OP_QUERY 0x3 +#define PSERIES_WDTF_OP_QUERY_LPM 0x4 + +/* Bits 56-63: "timeoutAction" */ +FIELD_BE(PSERIES_WDTF, ACTION, 56, 8) +#define PSERIES_WDTF_ACTION_HARD_POWER_OFF 0x1 +#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2 +#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3 + +FIELD_BE(PSERIES_WDTF, RESERVED, 0, 47) + +/* Special watchdogNumber for the "stop all watchdogs" operation */ +#define PSERIES_WDT_STOP_ALL ((uint64_t)~0) + +/* + * For the "Query watchdog capabilities" operation, a uint64 structure + * defined as: + * Bits 0-15: The minimum supported timeout in milliseconds + * Bits 16-31: The number of watchdogs supported + * Bits 32-63: Reserved + */ +FIELD_BE(PSERIES_WDTQ, MIN_TIMEOUT, 0, 16) +FIELD_BE(PSERIES_WDTQ, NUM, 16, 16) + +/* + * For the "Query watchdog LPM requirement" operation: + * 1 = The given "watchdogNumber" must be stopped prior to suspending + * 2 = The given "watchdogNumber" does not have to be stopped prior to + * suspending + */ +#define PSERIES_WDTQL_STOPPED 1 +#define PSERIES_WDTQL_QUERY_NOT_STOPPED 2 + +#define WDT_MIN_TIMEOUT 1 /* 1ms */ + +static target_ulong watchdog_stop(unsigned watchdogNumber, SpaprWatchdog *w) +{ + target_ulong ret = H_NOOP; + + if (timer_pending(&w->timer)) { + timer_del(&w->timer); + ret = H_SUCCESS; + } + trace_spapr_watchdog_stop(watchdogNumber, ret); + + return ret; +} + +static target_ulong watchdog_stop_all(SpaprMachineState *spapr) +{ + target_ulong ret = H_NOOP; + int i; + + for (i = 1; i <= ARRAY_SIZE(spapr->wds); ++i) { + target_ulong r = watchdog_stop(i, &spapr->wds[i - 1]); + + if (r != H_NOOP && r != H_SUCCESS) { + ret = r; + } + } + + return ret; +} + +static void watchdog_expired(void *pw) +{ + SpaprWatchdog *w = pw; + CPUState *cs; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + unsigned num = w - spapr->wds; + + g_assert(num < ARRAY_SIZE(spapr->wds)); + trace_spapr_watchdog_expired(num, w->action); + switch (w->action) { + case PSERIES_WDTF_ACTION_HARD_POWER_OFF: + qemu_system_vmstop_request(RUN_STATE_SHUTDOWN); + break; + case PSERIES_WDTF_ACTION_HARD_RESTART: + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + break; + case PSERIES_WDTF_ACTION_DUMP_RESTART: + CPU_FOREACH(cs) { + async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); + } + break; + } + if (!w->leave_others) { + watchdog_stop_all(spapr); + } +} + +static target_ulong h_watchdog(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong ret = H_SUCCESS; + target_ulong flags = args[0]; + target_ulong watchdogNumber = args[1]; /* 1-Based per PAPR */ + target_ulong timeoutInMs = args[2]; + unsigned operation = FIELD_EX64(flags, PSERIES_WDTF, OP); + unsigned timeoutAction = FIELD_EX64(flags, PSERIES_WDTF, ACTION); + SpaprWatchdog *w; + + if (FIELD_EX64(flags, PSERIES_WDTF, RESERVED)) { + return H_PARAMETER; + } + + switch (operation) { + case PSERIES_WDTF_OP_START: + if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + return H_P2; + } + if (timeoutInMs <= WDT_MIN_TIMEOUT) { + return H_P3; + } + + w = &spapr->wds[watchdogNumber - 1]; + switch (timeoutAction) { + case PSERIES_WDTF_ACTION_HARD_POWER_OFF: + case PSERIES_WDTF_ACTION_HARD_RESTART: + case PSERIES_WDTF_ACTION_DUMP_RESTART: + w->action = timeoutAction; + break; + default: + return H_PARAMETER; + } + w->leave_others = FIELD_EX64(flags, PSERIES_WDTF, LEAVE_OTHER); + timer_mod(&w->timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timeoutInMs); + trace_spapr_watchdog_start(flags, watchdogNumber, timeoutInMs); + break; + case PSERIES_WDTF_OP_STOP: + if (watchdogNumber == PSERIES_WDT_STOP_ALL) { + ret = watchdog_stop_all(spapr); + } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) { + ret = watchdog_stop(watchdogNumber, + &spapr->wds[watchdogNumber - 1]); + } else { + return H_P2; + } + break; + case PSERIES_WDTF_OP_QUERY: + args[0] = FIELD_DP64(0, PSERIES_WDTQ, MIN_TIMEOUT, WDT_MIN_TIMEOUT); + args[0] = FIELD_DP64(args[0], PSERIES_WDTQ, NUM, + ARRAY_SIZE(spapr->wds)); + trace_spapr_watchdog_query(args[0]); + break; + case PSERIES_WDTF_OP_QUERY_LPM: + if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + return H_P2; + } + args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; + trace_spapr_watchdog_query_lpm(args[0]); + break; + default: + return H_PARAMETER; + } + + return ret; +} + +void spapr_watchdog_init(SpaprMachineState *spapr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spapr->wds); ++i) { + char name[16]; + SpaprWatchdog *w = &spapr->wds[i]; + + snprintf(name, sizeof(name) - 1, "wdt%d", i + 1); + object_initialize_child_with_props(OBJECT(spapr), name, w, + sizeof(SpaprWatchdog), + TYPE_SPAPR_WDT, + &error_fatal, NULL); + qdev_realize(DEVICE(w), NULL, &error_fatal); + } +} + +static bool watchdog_needed(void *opaque) +{ + SpaprWatchdog *w = opaque; + + return timer_pending(&w->timer); +} + +static const VMStateDescription vmstate_wdt = { + .name = "spapr_watchdog", + .version_id = 1, + .minimum_version_id = 1, + .needed = watchdog_needed, + .fields = (VMStateField[]) { + VMSTATE_TIMER(timer, SpaprWatchdog), + VMSTATE_UINT8(action, SpaprWatchdog), + VMSTATE_UINT8(leave_others, SpaprWatchdog), + VMSTATE_END_OF_LIST() + } +}; + +static void spapr_wdt_realize(DeviceState *dev, Error **errp) +{ + SpaprWatchdog *w = SPAPR_WDT(dev); + Object *o = OBJECT(dev); + + timer_init_ms(&w->timer, QEMU_CLOCK_VIRTUAL, watchdog_expired, w); + + object_property_add_uint64_ptr(o, "expire", + (uint64_t *)&w->timer.expire_time, + OBJ_PROP_FLAG_READ); + object_property_add_uint8_ptr(o, "action", &w->action, OBJ_PROP_FLAG_READ); + object_property_add_uint8_ptr(o, "leaveOtherWatchdogsRunningOnTimeout", + &w->leave_others, OBJ_PROP_FLAG_READ); +} + +static void spapr_wdt_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = spapr_wdt_realize; + dc->vmsd = &vmstate_wdt; + dc->user_creatable = false; +} + +static const TypeInfo spapr_wdt_info = { + .name = TYPE_SPAPR_WDT, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SpaprWatchdog), + .class_init = spapr_wdt_class_init, +}; + +static void spapr_watchdog_register_types(void) +{ + spapr_register_hypercall(H_WATCHDOG, h_watchdog); + type_register_static(&spapr_wdt_info); +} + +type_init(spapr_watchdog_register_types) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index e7523e22aaf..89ccbcfdfd2 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -9,3 +9,10 @@ cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 # wdt-aspeed.c aspeed_wdt_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 + +# spapr_watchdog.c +spapr_watchdog_start(uint64_t flags, uint64_t num, uint64_t timeout) "Flags 0x%" PRIx64 " num=%" PRId64 " %" PRIu64 "ms" +spapr_watchdog_stop(uint64_t num, uint64_t ret) "num=%" PRIu64 " ret=%" PRId64 +spapr_watchdog_query(uint64_t caps) "caps=0x%" PRIx64 +spapr_watchdog_query_lpm(uint64_t caps) "caps=0x%" PRIx64 +spapr_watchdog_expired(uint64_t num, unsigned action) "num=%" PRIu64 " action=%u" diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index c5c4e943a84..4758514ddfc 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -1965,11 +1965,12 @@ static void xen_pt_config_reg_init(XenPCIPassthroughState *s, if ((data & host_mask) != (val & host_mask)) { uint32_t new_val; - - /* Mask out host (including past size). */ - new_val = val & host_mask; - /* Merge emulated ones (excluding the non-emulated ones). */ - new_val |= data & host_mask; + /* + * Merge the emulated bits (data) with the host bits (val) + * and mask out the bits past size to enable restoration + * of the proper value for logging below. + */ + new_val = XEN_PT_MERGE_VALUE(val, data, host_mask) & size_mask; /* Leave intact host and emulated values past the size - even though * we do not care as we write per reg->size granularity, but for the * logging below lets have the proper value. */ @@ -2031,12 +2032,16 @@ void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp) } } - /* - * By default we will trap up to 0x40 in the cfg space. - * If an intel device is pass through we need to trap 0xfc, - * therefore the size should be 0xff. - */ if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) { + if (!is_igd_vga_passthrough(&s->real_device) || + s->real_device.vendor_id != PCI_VENDOR_ID_INTEL) { + continue; + } + /* + * By default we will trap up to 0x40 in the cfg space. + * If an intel device is pass through we need to trap 0xfc, + * therefore the size should be 0xff. + */ reg_grp_offset = XEN_PCI_INTEL_OPREGION; } diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index b39eefb38d1..54840f86223 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -81,6 +81,8 @@ extern AioWait global_aio_wait; AioContext *ctx_ = (ctx); \ /* Increment wait_->num_waiters before evaluating cond. */ \ qatomic_inc(&wait_->num_waiters); \ + /* Paired with smp_mb in aio_wait_kick(). */ \ + smp_mb(); \ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ while ((cond)) { \ aio_poll(ctx_, true); \ diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 68bbd344b2c..ba0b425d780 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -40,7 +40,9 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, int64_t offset, int64_t *count); int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, - bool ignore_ratelimit); + bool ignore_ratelimit, uint64_t timeout_ns, + BlockCopyAsyncCallbackFunc cb, + void *cb_opaque); /* * Run block-copy in a coroutine, create corresponding BlockCopyCallState diff --git a/include/block/block-io.h b/include/block/block-io.h index 62c84f05193..fd25ffa9be8 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -39,14 +39,22 @@ * to catch when they are accidentally called by the wrong API. */ -int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); +int generated_co_wrapper bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, + int64_t bytes, + BdrvRequestFlags flags); int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes); -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, - int64_t bytes); -int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int64_t bytes); +int generated_co_wrapper bdrv_pread(BdrvChild *child, int64_t offset, + int64_t bytes, void *buf, + BdrvRequestFlags flags); +int generated_co_wrapper bdrv_pwrite(BdrvChild *child, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags); +int generated_co_wrapper bdrv_pwrite_sync(BdrvChild *child, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags); +int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags); /* * Efficiently zero a region of the disk image. Note that this is a regular * I/O request like read or write and should have a reasonable size. This @@ -270,7 +278,6 @@ void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); cond); }) void bdrv_drain(BlockDriverState *bs); -void coroutine_fn bdrv_co_drain(BlockDriverState *bs); int generated_co_wrapper bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index bb454200e51..91cdd616927 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -56,7 +56,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); static inline int coroutine_fn bdrv_co_pread(BdrvChild *child, - int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); IO_CODE(); @@ -65,7 +65,7 @@ static inline int coroutine_fn bdrv_co_pread(BdrvChild *child, } static inline int coroutine_fn bdrv_co_pwrite(BdrvChild *child, - int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); IO_CODE(); @@ -102,7 +102,7 @@ bool blk_dev_is_tray_open(BlockBackend *blk); void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); -bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, +void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, bool lock); diff --git a/include/block/nvme.h b/include/block/nvme.h index 3737351cc81..8027b7126bd 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -98,28 +98,28 @@ enum NvmeCapMask { #define NVME_CAP_PMRS(cap) (((cap) >> CAP_PMRS_SHIFT) & CAP_PMRS_MASK) #define NVME_CAP_CMBS(cap) (((cap) >> CAP_CMBS_SHIFT) & CAP_CMBS_MASK) -#define NVME_CAP_SET_MQES(cap, val) (cap |= (uint64_t)(val & CAP_MQES_MASK) \ - << CAP_MQES_SHIFT) -#define NVME_CAP_SET_CQR(cap, val) (cap |= (uint64_t)(val & CAP_CQR_MASK) \ - << CAP_CQR_SHIFT) -#define NVME_CAP_SET_AMS(cap, val) (cap |= (uint64_t)(val & CAP_AMS_MASK) \ - << CAP_AMS_SHIFT) -#define NVME_CAP_SET_TO(cap, val) (cap |= (uint64_t)(val & CAP_TO_MASK) \ - << CAP_TO_SHIFT) -#define NVME_CAP_SET_DSTRD(cap, val) (cap |= (uint64_t)(val & CAP_DSTRD_MASK) \ - << CAP_DSTRD_SHIFT) -#define NVME_CAP_SET_NSSRS(cap, val) (cap |= (uint64_t)(val & CAP_NSSRS_MASK) \ - << CAP_NSSRS_SHIFT) -#define NVME_CAP_SET_CSS(cap, val) (cap |= (uint64_t)(val & CAP_CSS_MASK) \ - << CAP_CSS_SHIFT) -#define NVME_CAP_SET_MPSMIN(cap, val) (cap |= (uint64_t)(val & CAP_MPSMIN_MASK)\ - << CAP_MPSMIN_SHIFT) -#define NVME_CAP_SET_MPSMAX(cap, val) (cap |= (uint64_t)(val & CAP_MPSMAX_MASK)\ - << CAP_MPSMAX_SHIFT) -#define NVME_CAP_SET_PMRS(cap, val) (cap |= (uint64_t)(val & CAP_PMRS_MASK) \ - << CAP_PMRS_SHIFT) -#define NVME_CAP_SET_CMBS(cap, val) (cap |= (uint64_t)(val & CAP_CMBS_MASK) \ - << CAP_CMBS_SHIFT) +#define NVME_CAP_SET_MQES(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_MQES_MASK) << CAP_MQES_SHIFT) +#define NVME_CAP_SET_CQR(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_CQR_MASK) << CAP_CQR_SHIFT) +#define NVME_CAP_SET_AMS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_AMS_MASK) << CAP_AMS_SHIFT) +#define NVME_CAP_SET_TO(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_TO_MASK) << CAP_TO_SHIFT) +#define NVME_CAP_SET_DSTRD(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_DSTRD_MASK) << CAP_DSTRD_SHIFT) +#define NVME_CAP_SET_NSSRS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_NSSRS_MASK) << CAP_NSSRS_SHIFT) +#define NVME_CAP_SET_CSS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_CSS_MASK) << CAP_CSS_SHIFT) +#define NVME_CAP_SET_MPSMIN(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_MPSMIN_MASK) << CAP_MPSMIN_SHIFT) +#define NVME_CAP_SET_MPSMAX(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_MPSMAX_MASK) << CAP_MPSMAX_SHIFT) +#define NVME_CAP_SET_PMRS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_PMRS_MASK) << CAP_PMRS_SHIFT) +#define NVME_CAP_SET_CMBS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_CMBS_MASK) << CAP_CMBS_SHIFT) enum NvmeCapCss { NVME_CAP_CSS_NVM = 1 << 0, @@ -595,6 +595,8 @@ enum NvmeAdminCommands { NVME_ADM_CMD_ACTIVATE_FW = 0x10, NVME_ADM_CMD_DOWNLOAD_FW = 0x11, NVME_ADM_CMD_NS_ATTACHMENT = 0x15, + NVME_ADM_CMD_VIRT_MNGMT = 0x1c, + NVME_ADM_CMD_DBBUF_CONFIG = 0x7c, NVME_ADM_CMD_FORMAT_NVM = 0x80, NVME_ADM_CMD_SECURITY_SEND = 0x81, NVME_ADM_CMD_SECURITY_RECV = 0x82, @@ -899,6 +901,10 @@ enum NvmeStatusCodes { NVME_NS_PRIVATE = 0x0119, NVME_NS_NOT_ATTACHED = 0x011a, NVME_NS_CTRL_LIST_INVALID = 0x011c, + NVME_INVALID_CTRL_ID = 0x011f, + NVME_INVALID_SEC_CTRL_STATE = 0x0120, + NVME_INVALID_NUM_RESOURCES = 0x0121, + NVME_INVALID_RESOURCE_ID = 0x0122, NVME_CONFLICTING_ATTRS = 0x0180, NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, @@ -1033,6 +1039,8 @@ enum NvmeIdCns { NVME_ID_CNS_NS_PRESENT = 0x11, NVME_ID_CNS_NS_ATTACHED_CTRL_LIST = 0x12, NVME_ID_CNS_CTRL_LIST = 0x13, + NVME_ID_CNS_PRIMARY_CTRL_CAP = 0x14, + NVME_ID_CNS_SECONDARY_CTRL_LIST = 0x15, NVME_ID_CNS_CS_NS_PRESENT_LIST = 0x1a, NVME_ID_CNS_CS_NS_PRESENT = 0x1b, NVME_ID_CNS_IO_COMMAND_SET = 0x1c, @@ -1134,6 +1142,7 @@ enum NvmeIdCtrlOacs { NVME_OACS_FORMAT = 1 << 1, NVME_OACS_FW = 1 << 2, NVME_OACS_NS_MGMT = 1 << 3, + NVME_OACS_DBBUF = 1 << 8, }; enum NvmeIdCtrlOncs { @@ -1553,6 +1562,61 @@ typedef enum NvmeZoneState { NVME_ZONE_STATE_OFFLINE = 0x0f, } NvmeZoneState; +typedef struct QEMU_PACKED NvmePriCtrlCap { + uint16_t cntlid; + uint16_t portid; + uint8_t crt; + uint8_t rsvd5[27]; + uint32_t vqfrt; + uint32_t vqrfa; + uint16_t vqrfap; + uint16_t vqprt; + uint16_t vqfrsm; + uint16_t vqgran; + uint8_t rsvd48[16]; + uint32_t vifrt; + uint32_t virfa; + uint16_t virfap; + uint16_t viprt; + uint16_t vifrsm; + uint16_t vigran; + uint8_t rsvd80[4016]; +} NvmePriCtrlCap; + +typedef enum NvmePriCtrlCapCrt { + NVME_CRT_VQ = 1 << 0, + NVME_CRT_VI = 1 << 1, +} NvmePriCtrlCapCrt; + +typedef struct QEMU_PACKED NvmeSecCtrlEntry { + uint16_t scid; + uint16_t pcid; + uint8_t scs; + uint8_t rsvd5[3]; + uint16_t vfn; + uint16_t nvq; + uint16_t nvi; + uint8_t rsvd14[18]; +} NvmeSecCtrlEntry; + +typedef struct QEMU_PACKED NvmeSecCtrlList { + uint8_t numcntl; + uint8_t rsvd1[31]; + NvmeSecCtrlEntry sec[127]; +} NvmeSecCtrlList; + +typedef enum NvmeVirtMngmtAction { + NVME_VIRT_MNGMT_ACTION_PRM_ALLOC = 0x01, + NVME_VIRT_MNGMT_ACTION_SEC_OFFLINE = 0x07, + NVME_VIRT_MNGMT_ACTION_SEC_ASSIGN = 0x08, + NVME_VIRT_MNGMT_ACTION_SEC_ONLINE = 0x09, +} NvmeVirtMngmtAction; + +typedef enum NvmeVirtualResourceType { + NVME_VIRT_RES_QUEUE = 0x00, + NVME_VIRT_RES_INTERRUPT = 0x01, +} NvmeVirtualResourceType; + static inline void _nvme_check_size(void) { QEMU_BUILD_BUG_ON(sizeof(NvmeBar) != 4096); @@ -1588,5 +1652,8 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsDescr) != 4); QEMU_BUILD_BUG_ON(sizeof(NvmeZoneDescr) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDifTuple) != 16); + QEMU_BUILD_BUG_ON(sizeof(NvmePriCtrlCap) != 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeSecCtrlEntry) != 32); + QEMU_BUILD_BUG_ON(sizeof(NvmeSecCtrlList) != 4096); } #endif diff --git a/include/crypto/block.h b/include/crypto/block.h index 7a65e8e402a..4f63a378727 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -29,24 +29,24 @@ typedef struct QCryptoBlock QCryptoBlock; /* See also QCryptoBlockFormat, QCryptoBlockCreateOptions * and QCryptoBlockOpenOptions in qapi/crypto.json */ -typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp); +typedef int (*QCryptoBlockReadFunc)(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp); -typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp); +typedef int (*QCryptoBlockInitFunc)(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp); -typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp); +typedef int (*QCryptoBlockWriteFunc)(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp); /** * qcrypto_block_has_format: diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 5968551a055..2281be4e109 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -35,6 +35,7 @@ extern intptr_t qemu_host_page_mask; void qemu_init_cpu_list(void); void cpu_list_lock(void); void cpu_list_unlock(void); +unsigned int cpu_list_generation_id_get(void); void tcg_flush_softmmu_tlb(CPUState *cs); diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index c35d7334b49..f667014888c 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -10,11 +10,71 @@ #define GDB_WATCHPOINT_READ 3 #define GDB_WATCHPOINT_ACCESS 4 +/* For gdb file i/o remote protocol open flags. */ +#define GDB_O_RDONLY 0 +#define GDB_O_WRONLY 1 +#define GDB_O_RDWR 2 +#define GDB_O_APPEND 8 +#define GDB_O_CREAT 0x200 +#define GDB_O_TRUNC 0x400 +#define GDB_O_EXCL 0x800 + +/* For gdb file i/o remote protocol errno values */ +#define GDB_EPERM 1 +#define GDB_ENOENT 2 +#define GDB_EINTR 4 +#define GDB_EBADF 9 +#define GDB_EACCES 13 +#define GDB_EFAULT 14 +#define GDB_EBUSY 16 +#define GDB_EEXIST 17 +#define GDB_ENODEV 19 +#define GDB_ENOTDIR 20 +#define GDB_EISDIR 21 +#define GDB_EINVAL 22 +#define GDB_ENFILE 23 +#define GDB_EMFILE 24 +#define GDB_EFBIG 27 +#define GDB_ENOSPC 28 +#define GDB_ESPIPE 29 +#define GDB_EROFS 30 +#define GDB_ENAMETOOLONG 91 +#define GDB_EUNKNOWN 9999 + +/* For gdb file i/o remote protocol lseek whence. */ +#define GDB_SEEK_SET 0 +#define GDB_SEEK_CUR 1 +#define GDB_SEEK_END 2 + +/* For gdb file i/o stat/fstat. */ +typedef uint32_t gdb_mode_t; +typedef uint32_t gdb_time_t; + +struct gdb_stat { + uint32_t gdb_st_dev; /* device */ + uint32_t gdb_st_ino; /* inode */ + gdb_mode_t gdb_st_mode; /* protection */ + uint32_t gdb_st_nlink; /* number of hard links */ + uint32_t gdb_st_uid; /* user ID of owner */ + uint32_t gdb_st_gid; /* group ID of owner */ + uint32_t gdb_st_rdev; /* device type (if inode device) */ + uint64_t gdb_st_size; /* total size, in bytes */ + uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ + uint64_t gdb_st_blocks; /* number of blocks allocated */ + gdb_time_t gdb_st_atime; /* time of last access */ + gdb_time_t gdb_st_mtime; /* time of last modification */ + gdb_time_t gdb_st_ctime; /* time of last change */ +} QEMU_PACKED; + +struct gdb_timeval { + gdb_time_t tv_sec; /* second */ + uint64_t tv_usec; /* microsecond */ +} QEMU_PACKED; + #ifdef NEED_CPU_H #include "cpu.h" -typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, - target_ulong ret, target_ulong err); +typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err); /** * gdb_do_syscall: diff --git a/include/exec/memory.h b/include/exec/memory.h index a6a0f4d8ad0..bfb1de8eeae 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -69,7 +69,10 @@ static inline void fuzz_dma_read_cb(size_t addr, /* Dirty tracking enabled because measuring dirty rate */ #define GLOBAL_DIRTY_DIRTY_RATE (1U << 1) -#define GLOBAL_DIRTY_MASK (0x3) +/* Dirty tracking enabled because dirty limit */ +#define GLOBAL_DIRTY_LIMIT (1U << 2) + +#define GLOBAL_DIRTY_MASK (0x7) extern unsigned int global_dirty_tracking; diff --git a/include/exec/poison.h b/include/exec/poison.h index bbb82cf9ec6..f0959bc84ef 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -66,8 +66,6 @@ #pragma GCC poison CPU_INTERRUPT_TGT_INT_2 #pragma GCC poison CONFIG_ALPHA_DIS -#pragma GCC poison CONFIG_ARM_A64_DIS -#pragma GCC poison CONFIG_ARM_DIS #pragma GCC poison CONFIG_CRIS_DIS #pragma GCC poison CONFIG_HPPA_DIS #pragma GCC poison CONFIG_I386_DIS diff --git a/include/exec/softmmu-semi.h b/include/exec/softmmu-semi.h deleted file mode 100644 index fbcae88f4ba..00000000000 --- a/include/exec/softmmu-semi.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Helper routines to provide target memory access for semihosting - * syscalls in system emulation mode. - * - * Copyright (c) 2007 CodeSourcery. - * - * This code is licensed under the GPL - */ - -#ifndef SOFTMMU_SEMI_H -#define SOFTMMU_SEMI_H - -#include "cpu.h" - -static inline uint64_t softmmu_tget64(CPUArchState *env, target_ulong addr) -{ - uint64_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 8, 0); - return tswap64(val); -} - -static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr) -{ - uint32_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 4, 0); - return tswap32(val); -} - -static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr) -{ - uint8_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, &val, 1, 0); - return val; -} - -#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p); 0; }) -#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; }) -#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; }) -#define get_user_ual(arg, p) get_user_u32(arg, p) - -static inline void softmmu_tput64(CPUArchState *env, - target_ulong addr, uint64_t val) -{ - val = tswap64(val); - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 8, 1); -} - -static inline void softmmu_tput32(CPUArchState *env, - target_ulong addr, uint32_t val) -{ - val = tswap32(val); - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 4, 1); -} -#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; }) -#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; }) -#define put_user_ual(arg, p) put_user_u32(arg, p) - -static void *softmmu_lock_user(CPUArchState *env, - target_ulong addr, target_ulong len, int copy) -{ - uint8_t *p; - /* TODO: Make this something that isn't fixed size. */ - p = malloc(len); - if (p && copy) { - cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0); - } - return p; -} -#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) -static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) -{ - char *p; - char *s; - uint8_t c; - /* TODO: Make this something that isn't fixed size. */ - s = p = malloc(1024); - if (!s) { - return NULL; - } - do { - cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0); - addr++; - *(p++) = c; - } while (c); - return s; -} -#define lock_user_string(p) softmmu_lock_user_string(env, p) -static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr, - target_ulong len) -{ - if (len) { - cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); - } - free(p); -} -#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) - -#endif diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 02a5a9ffcbd..8389200b2d0 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -34,12 +34,16 @@ #include "hw/usb/hcd-ehci.h" #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" +#include "hw/misc/unimp.h" +#include "hw/misc/aspeed_peci.h" +#include "hw/char/serial.h" #define ASPEED_SPIS_NUM 2 #define ASPEED_EHCIS_NUM 2 #define ASPEED_WDTS_NUM 4 #define ASPEED_CPUS_NUM 2 #define ASPEED_MACS_NUM 4 +#define ASPEED_UARTS_NUM 13 struct AspeedSoCState { /*< private >*/ @@ -49,7 +53,9 @@ struct AspeedSoCState { ARMCPU cpu[ASPEED_CPUS_NUM]; A15MPPrivState a7mpcore; ARMv7MState armv7m; + MemoryRegion *memory; MemoryRegion *dram_mr; + MemoryRegion dram_container; MemoryRegion sram; AspeedVICState vic; AspeedRtcState rtc; @@ -64,6 +70,7 @@ struct AspeedSoCState { AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; AspeedSBCState sbc; + UnimplementedDeviceState sbc_unimplemented; AspeedSDMCState sdmc; AspeedWDTState wdt[ASPEED_WDTS_NUM]; FTGMAC100State ftgmac100[ASPEED_MACS_NUM]; @@ -73,8 +80,13 @@ struct AspeedSoCState { AspeedSDHCIState sdhci; AspeedSDHCIState emmc; AspeedLPCState lpc; - uint32_t uart_default; + AspeedPECIState peci; + SerialMM uart[ASPEED_UARTS_NUM]; Clock *sysclk; + UnimplementedDeviceState iomem; + UnimplementedDeviceState video; + UnimplementedDeviceState emmc_boot_controller; + UnimplementedDeviceState dpmcu; }; #define TYPE_ASPEED_SOC "aspeed-soc" @@ -145,6 +157,7 @@ enum { ASPEED_DEV_LPC, ASPEED_DEV_IBT, ASPEED_DEV_I2C, + ASPEED_DEV_PECI, ASPEED_DEV_ETH1, ASPEED_DEV_ETH2, ASPEED_DEV_ETH3, @@ -164,6 +177,14 @@ enum { }; qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); -void aspeed_soc_uart_init(AspeedSoCState *s); +bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); +void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr); +bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp); +void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr); +void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, + const char *name, hwaddr addr, + uint64_t size); +void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, + unsigned int count, int unit0); #endif /* ASPEED_SOC_H */ diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index d864879421a..c9d25d493e0 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -17,6 +17,7 @@ #include "hw/char/bcm2835_aux.h" #include "hw/display/bcm2835_fb.h" #include "hw/dma/bcm2835_dma.h" +#include "hw/or-irq.h" #include "hw/intc/bcm2835_ic.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_rng.h" @@ -55,6 +56,7 @@ struct BCM2835PeripheralState { BCM2835AuxState aux; BCM2835FBState fb; BCM2835DMAState dma; + qemu_or_irq orgated_dma_irq; BCM2835ICState ic; BCM2835PropertyState property; BCM2835RngState rng; diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 15feabac63d..6ec479ca2b7 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -152,7 +152,7 @@ struct VirtMachineState { bool virt; bool ras; bool mte; - bool dtb_kaslr_seed; + bool dtb_randomness; OnOffAuto acpi; VirtGICType gic_version; VirtIOMMUType iommu; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 996f94059f7..500503da131 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -418,6 +418,12 @@ struct CPUState { */ bool throttle_thread_scheduled; + /* + * Sleep throttle_us_per_full microseconds once dirty ring is full + * if dirty page rate limit is enabled. + */ + int64_t throttle_us_per_full; + bool ignore_memory_transaction_failures; /* Used for user-only emulation of prctl(PR_SET_UNALIGN). */ diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index 134b295b403..38e0e271d51 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -53,4 +53,9 @@ struct CXLHost { #define TYPE_PXB_CXL_HOST "pxb-cxl-host" OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST) +#define TYPE_CXL_USP "cxl-upstream" + +typedef struct CXLUpstreamPort CXLUpstreamPort; +DECLARE_INSTANCE_CHECKER(CXLUpstreamPort, CXL_USP, TYPE_CXL_USP) +CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp); #endif diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 4b9be09274c..300a89b3430 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -23,16 +23,199 @@ #include "hw/i2c/i2c.h" #include "hw/sysbus.h" +#include "hw/registerfields.h" #include "qom/object.h" #define TYPE_ASPEED_I2C "aspeed.i2c" #define TYPE_ASPEED_2400_I2C TYPE_ASPEED_I2C "-ast2400" #define TYPE_ASPEED_2500_I2C TYPE_ASPEED_I2C "-ast2500" #define TYPE_ASPEED_2600_I2C TYPE_ASPEED_I2C "-ast2600" +#define TYPE_ASPEED_1030_I2C TYPE_ASPEED_I2C "-ast1030" OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C) #define ASPEED_I2C_NR_BUSSES 16 #define ASPEED_I2C_MAX_POOL_SIZE 0x800 +#define ASPEED_I2C_OLD_NUM_REG 11 +#define ASPEED_I2C_NEW_NUM_REG 22 + +/* Tx State Machine */ +#define I2CD_TX_STATE_MASK 0xf +#define I2CD_IDLE 0x0 +#define I2CD_MACTIVE 0x8 +#define I2CD_MSTART 0x9 +#define I2CD_MSTARTR 0xa +#define I2CD_MSTOP 0xb +#define I2CD_MTXD 0xc +#define I2CD_MRXACK 0xd +#define I2CD_MRXD 0xe +#define I2CD_MTXACK 0xf +#define I2CD_SWAIT 0x1 +#define I2CD_SRXD 0x4 +#define I2CD_STXACK 0x5 +#define I2CD_STXD 0x6 +#define I2CD_SRXACK 0x7 +#define I2CD_RECOVER 0x3 + +/* I2C Global Register */ +REG32(I2C_CTRL_STATUS, 0x0) /* Device Interrupt Status */ +REG32(I2C_CTRL_ASSIGN, 0x8) /* Device Interrupt Target Assignment */ +REG32(I2C_CTRL_GLOBAL, 0xC) /* Global Control Register */ + FIELD(I2C_CTRL_GLOBAL, REG_MODE, 2, 1) + FIELD(I2C_CTRL_GLOBAL, SRAM_EN, 0, 1) +REG32(I2C_CTRL_NEW_CLK_DIVIDER, 0x10) /* New mode clock divider */ + +/* I2C Old Mode Device (Bus) Register */ +REG32(I2CD_FUN_CTRL, 0x0) /* I2CD Function Control */ + FIELD(I2CD_FUN_CTRL, POOL_PAGE_SEL, 20, 3) /* AST2400 */ + SHARED_FIELD(M_SDA_LOCK_EN, 16, 1) + SHARED_FIELD(MULTI_MASTER_DIS, 15, 1) + SHARED_FIELD(M_SCL_DRIVE_EN, 14, 1) + SHARED_FIELD(MSB_STS, 9, 1) + SHARED_FIELD(SDA_DRIVE_IT_EN, 8, 1) + SHARED_FIELD(M_SDA_DRIVE_IT_EN, 7, 1) + SHARED_FIELD(M_HIGH_SPEED_EN, 6, 1) + SHARED_FIELD(DEF_ADDR_EN, 5, 1) + SHARED_FIELD(DEF_ALERT_EN, 4, 1) + SHARED_FIELD(DEF_ARP_EN, 3, 1) + SHARED_FIELD(DEF_GCALL_EN, 2, 1) + SHARED_FIELD(SLAVE_EN, 1, 1) + SHARED_FIELD(MASTER_EN, 0, 1) +REG32(I2CD_AC_TIMING1, 0x04) /* Clock and AC Timing Control #1 */ +REG32(I2CD_AC_TIMING2, 0x08) /* Clock and AC Timing Control #2 */ +REG32(I2CD_INTR_CTRL, 0x0C) /* I2CD Interrupt Control */ +REG32(I2CD_INTR_STS, 0x10) /* I2CD Interrupt Status */ + SHARED_FIELD(SLAVE_ADDR_MATCH, 31, 1) /* 0: addr1 1: addr2 */ + SHARED_FIELD(SLAVE_ADDR_RX_PENDING, 29, 1) + SHARED_FIELD(SLAVE_INACTIVE_TIMEOUT, 15, 1) + SHARED_FIELD(SDA_DL_TIMEOUT, 14, 1) + SHARED_FIELD(BUS_RECOVER_DONE, 13, 1) + SHARED_FIELD(SMBUS_ALERT, 12, 1) /* Bus [0-3] only */ + FIELD(I2CD_INTR_STS, SMBUS_ARP_ADDR, 11, 1) /* Removed */ + FIELD(I2CD_INTR_STS, SMBUS_DEV_ALERT_ADDR, 10, 1) /* Removed */ + FIELD(I2CD_INTR_STS, SMBUS_DEF_ADDR, 9, 1) /* Removed */ + FIELD(I2CD_INTR_STS, GCALL_ADDR, 8, 1) /* Removed */ + FIELD(I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 7, 1) /* use RX_DONE */ + SHARED_FIELD(SCL_TIMEOUT, 6, 1) + SHARED_FIELD(ABNORMAL, 5, 1) + SHARED_FIELD(NORMAL_STOP, 4, 1) + SHARED_FIELD(ARBIT_LOSS, 3, 1) + SHARED_FIELD(RX_DONE, 2, 1) + SHARED_FIELD(TX_NAK, 1, 1) + SHARED_FIELD(TX_ACK, 0, 1) +REG32(I2CD_CMD, 0x14) /* I2CD Command/Status */ + SHARED_FIELD(SDA_OE, 28, 1) + SHARED_FIELD(SDA_O, 27, 1) + SHARED_FIELD(SCL_OE, 26, 1) + SHARED_FIELD(SCL_O, 25, 1) + SHARED_FIELD(TX_TIMING, 23, 2) + SHARED_FIELD(TX_STATE, 19, 4) + SHARED_FIELD(SCL_LINE_STS, 18, 1) + SHARED_FIELD(SDA_LINE_STS, 17, 1) + SHARED_FIELD(BUS_BUSY_STS, 16, 1) + SHARED_FIELD(SDA_OE_OUT_DIR, 15, 1) + SHARED_FIELD(SDA_O_OUT_DIR, 14, 1) + SHARED_FIELD(SCL_OE_OUT_DIR, 13, 1) + SHARED_FIELD(SCL_O_OUT_DIR, 12, 1) + SHARED_FIELD(BUS_RECOVER_CMD_EN, 11, 1) + SHARED_FIELD(S_ALT_EN, 10, 1) + /* Command Bits */ + SHARED_FIELD(RX_DMA_EN, 9, 1) + SHARED_FIELD(TX_DMA_EN, 8, 1) + SHARED_FIELD(RX_BUFF_EN, 7, 1) + SHARED_FIELD(TX_BUFF_EN, 6, 1) + SHARED_FIELD(M_STOP_CMD, 5, 1) + SHARED_FIELD(M_S_RX_CMD_LAST, 4, 1) + SHARED_FIELD(M_RX_CMD, 3, 1) + SHARED_FIELD(S_TX_CMD, 2, 1) + SHARED_FIELD(M_TX_CMD, 1, 1) + SHARED_FIELD(M_START_CMD, 0, 1) +REG32(I2CD_DEV_ADDR, 0x18) /* Slave Device Address */ +REG32(I2CD_POOL_CTRL, 0x1C) /* Pool Buffer Control */ + SHARED_FIELD(RX_COUNT, 24, 5) + SHARED_FIELD(RX_SIZE, 16, 5) + SHARED_FIELD(TX_COUNT, 9, 5) + FIELD(I2CD_POOL_CTRL, OFFSET, 2, 6) /* AST2400 */ +REG32(I2CD_BYTE_BUF, 0x20) /* Transmit/Receive Byte Buffer */ + SHARED_FIELD(RX_BUF, 8, 8) + SHARED_FIELD(TX_BUF, 0, 8) +REG32(I2CD_DMA_ADDR, 0x24) /* DMA Buffer Address */ +REG32(I2CD_DMA_LEN, 0x28) /* DMA Transfer Length < 4KB */ + +/* I2C New Mode Device (Bus) Register */ +REG32(I2CC_FUN_CTRL, 0x0) + FIELD(I2CC_FUN_CTRL, RB_EARLY_DONE_EN, 22, 1) + FIELD(I2CC_FUN_CTRL, DMA_DIS_AUTO_RECOVER, 21, 1) + FIELD(I2CC_FUN_CTRL, S_SAVE_ADDR, 20, 1) + FIELD(I2CC_FUN_CTRL, M_PKT_RETRY_CNT, 18, 2) + /* 17:0 shared with I2CD_FUN_CTRL[17:0] */ +REG32(I2CC_AC_TIMING, 0x04) +REG32(I2CC_MS_TXRX_BYTE_BUF, 0x08) + /* 31:16 shared with I2CD_CMD[31:16] */ + /* 15:0 shared with I2CD_BYTE_BUF[15:0] */ +REG32(I2CC_POOL_CTRL, 0x0c) + /* 31:0 shared with I2CD_POOL_CTRL[31:0] */ +REG32(I2CM_INTR_CTRL, 0x10) +REG32(I2CM_INTR_STS, 0x14) + FIELD(I2CM_INTR_STS, PKT_STATE, 28, 4) + FIELD(I2CM_INTR_STS, PKT_CMD_TIMEOUT, 18, 1) + FIELD(I2CM_INTR_STS, PKT_CMD_FAIL, 17, 1) + FIELD(I2CM_INTR_STS, PKT_CMD_DONE, 16, 1) + FIELD(I2CM_INTR_STS, BUS_RECOVER_FAIL, 15, 1) + /* 14:0 shared with I2CD_INTR_STS[14:0] */ +REG32(I2CM_CMD, 0x18) + FIELD(I2CM_CMD, W1_CTRL, 31, 1) + FIELD(I2CM_CMD, PKT_DEV_ADDR, 24, 7) + FIELD(I2CM_CMD, HS_MASTER_MODE_LSB, 17, 3) + FIELD(I2CM_CMD, PKT_OP_EN, 16, 1) + /* 15:0 shared with I2CD_CMD[15:0] */ +REG32(I2CM_DMA_LEN, 0x1c) + FIELD(I2CM_DMA_LEN, RX_BUF_LEN_W1T, 31, 1) + FIELD(I2CM_DMA_LEN, RX_BUF_LEN, 16, 11) + FIELD(I2CM_DMA_LEN, TX_BUF_LEN_W1T, 15, 1) + FIELD(I2CM_DMA_LEN, TX_BUF_LEN, 0, 11) +REG32(I2CS_INTR_CTRL, 0x20) + FIELD(I2CS_INTR_CTRL, PKT_CMD_FAIL, 17, 1) + FIELD(I2CS_INTR_CTRL, PKT_CMD_DONE, 16, 1) +REG32(I2CS_INTR_STS, 0x24) + /* 31:29 shared with I2CD_INTR_STS[31:29] */ + FIELD(I2CS_INTR_STS, SLAVE_PARKING_STS, 24, 2) + FIELD(I2CS_INTR_STS, SLAVE_ADDR3_NAK, 22, 1) + FIELD(I2CS_INTR_STS, SLAVE_ADDR2_NAK, 21, 1) + FIELD(I2CS_INTR_STS, SLAVE_ADDR1_NAK, 20, 1) + FIELD(I2CS_INTR_STS, SLAVE_ADDR_INDICATOR, 18, 2) + FIELD(I2CS_INTR_STS, PKT_CMD_FAIL, 17, 1) + FIELD(I2CS_INTR_STS, PKT_CMD_DONE, 16, 1) + /* 14:0 shared with I2CD_INTR_STS[14:0] */ + FIELD(I2CS_INTR_STS, SLAVE_ADDR_RX_MATCH, 7, 1) +REG32(I2CS_CMD, 0x28) + FIELD(I2CS_CMD, W1_CTRL, 31, 1) + FIELD(I2CS_CMD, PKT_MODE_ACTIVE_ADDR, 17, 2) + FIELD(I2CS_CMD, PKT_MODE_EN, 16, 1) + FIELD(I2CS_CMD, AUTO_NAK_INACTIVE_ADDR, 15, 1) + FIELD(I2CS_CMD, AUTO_NAK_ACTIVE_ADDR, 14, 1) + /* 13:0 shared with I2CD_CMD[13:0] */ +REG32(I2CS_DMA_LEN, 0x2c) + FIELD(I2CS_DMA_LEN, RX_BUF_LEN_W1T, 31, 1) + FIELD(I2CS_DMA_LEN, RX_BUF_LEN, 16, 11) + FIELD(I2CS_DMA_LEN, TX_BUF_LEN_W1T, 15, 1) + FIELD(I2CS_DMA_LEN, TX_BUF_LEN, 0, 11) +REG32(I2CM_DMA_TX_ADDR, 0x30) + FIELD(I2CM_DMA_TX_ADDR, ADDR, 0, 31) +REG32(I2CM_DMA_RX_ADDR, 0x34) + FIELD(I2CM_DMA_RX_ADDR, ADDR, 0, 31) +REG32(I2CS_DMA_TX_ADDR, 0x38) + FIELD(I2CS_DMA_TX_ADDR, ADDR, 0, 31) +REG32(I2CS_DMA_RX_ADDR, 0x3c) + FIELD(I2CS_DMA_RX_ADDR, ADDR, 0, 31) +REG32(I2CS_DEV_ADDR, 0x40) +REG32(I2CM_DMA_LEN_STS, 0x48) + FIELD(I2CM_DMA_LEN_STS, RX_LEN, 16, 13) + FIELD(I2CM_DMA_LEN_STS, TX_LEN, 0, 13) +REG32(I2CS_DMA_LEN_STS, 0x4c) + FIELD(I2CS_DMA_LEN_STS, RX_LEN, 16, 13) + FIELD(I2CS_DMA_LEN_STS, TX_LEN, 0, 13) +REG32(I2CC_DMA_ADDR, 0x50) +REG32(I2CC_DMA_LEN, 0x54) struct AspeedI2CState; @@ -43,21 +226,16 @@ struct AspeedI2CBus { struct AspeedI2CState *controller; + /* slave mode */ + I2CSlave *slave; + MemoryRegion mr; I2CBus *bus; uint8_t id; qemu_irq irq; - uint32_t ctrl; - uint32_t timing[2]; - uint32_t intr_ctrl; - uint32_t intr_status; - uint32_t cmd; - uint32_t buf; - uint32_t pool_ctrl; - uint32_t dma_addr; - uint32_t dma_len; + uint32_t regs[ASPEED_I2C_NEW_NUM_REG]; }; struct AspeedI2CState { @@ -68,6 +246,7 @@ struct AspeedI2CState { uint32_t intr_status; uint32_t ctrl_global; + uint32_t new_clk_divider; MemoryRegion pool_iomem; uint8_t pool[ASPEED_I2C_MAX_POOL_SIZE]; @@ -76,6 +255,11 @@ struct AspeedI2CState { AddressSpace dram_as; }; +#define TYPE_ASPEED_I2C_BUS_SLAVE "aspeed.i2c.slave" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedI2CBusSlave, ASPEED_I2C_BUS_SLAVE) +struct AspeedI2CBusSlave { + I2CSlave i2c; +}; struct AspeedI2CClass { SysBusDeviceClass parent_class; @@ -93,6 +277,104 @@ struct AspeedI2CClass { }; +static inline bool aspeed_i2c_is_new_mode(AspeedI2CState *s) +{ + return FIELD_EX32(s->ctrl_global, I2C_CTRL_GLOBAL, REG_MODE); +} + +static inline bool aspeed_i2c_bus_pkt_mode_en(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, PKT_OP_EN); + } + return false; +} + +static inline uint32_t aspeed_i2c_bus_ctrl_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_FUN_CTRL; + } + return R_I2CD_FUN_CTRL; +} + +static inline uint32_t aspeed_i2c_bus_cmd_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CM_CMD; + } + return R_I2CD_CMD; +} + +static inline uint32_t aspeed_i2c_bus_dev_addr_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CS_DEV_ADDR; + } + return R_I2CD_DEV_ADDR; +} + +static inline uint32_t aspeed_i2c_bus_intr_ctrl_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CM_INTR_CTRL; + } + return R_I2CD_INTR_CTRL; +} + +static inline uint32_t aspeed_i2c_bus_intr_sts_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CM_INTR_STS; + } + return R_I2CD_INTR_STS; +} + +static inline uint32_t aspeed_i2c_bus_pool_ctrl_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_POOL_CTRL; + } + return R_I2CD_POOL_CTRL; +} + +static inline uint32_t aspeed_i2c_bus_byte_buf_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_MS_TXRX_BYTE_BUF; + } + return R_I2CD_BYTE_BUF; +} + +static inline uint32_t aspeed_i2c_bus_dma_len_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_DMA_LEN; + } + return R_I2CD_DMA_LEN; +} + +static inline uint32_t aspeed_i2c_bus_dma_addr_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_DMA_ADDR; + } + return R_I2CD_DMA_ADDR; +} + +static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus) +{ + return SHARED_ARRAY_FIELD_EX32(bus->regs, aspeed_i2c_bus_ctrl_offset(bus), + MASTER_EN); +} + +static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) +{ + uint32_t ctrl_reg = aspeed_i2c_bus_ctrl_offset(bus); + return SHARED_ARRAY_FIELD_EX32(bus->regs, ctrl_reg, MASTER_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, ctrl_reg, SLAVE_EN); +} + I2CBus *aspeed_i2c_get_bus(AspeedI2CState *s, int busnr); #endif /* ASPEED_I2C_H */ diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 5ca3b708c0b..9b9581d2309 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -12,6 +12,7 @@ enum i2c_event { I2C_START_RECV, I2C_START_SEND, + I2C_START_SEND_ASYNC, I2C_FINISH, I2C_NACK /* Masker NACKed a receive byte. */ }; @@ -28,6 +29,9 @@ struct I2CSlaveClass { /* Master to slave. Returns non-zero for a NAK, 0 for success. */ int (*send)(I2CSlave *s, uint8_t data); + /* Master to slave (asynchronous). Receiving slave must call i2c_ack(). */ + void (*send_async)(I2CSlave *s, uint8_t data); + /* * Slave to master. This cannot fail, the device should always * return something here. @@ -69,13 +73,25 @@ struct I2CNode { QLIST_ENTRY(I2CNode) next; }; +typedef struct I2CPendingMaster I2CPendingMaster; + +struct I2CPendingMaster { + QEMUBH *bh; + QSIMPLEQ_ENTRY(I2CPendingMaster) entry; +}; + typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; +typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; struct I2CBus { BusState qbus; I2CNodeList current_devs; + I2CPendingMasters pending_masters; uint8_t saved_address; bool broadcast; + + /* Set from slave currently mastering the bus. */ + QEMUBH *bh; }; I2CBus *i2c_init_bus(DeviceState *parent, const char *name); @@ -115,9 +131,23 @@ int i2c_start_recv(I2CBus *bus, uint8_t address); */ int i2c_start_send(I2CBus *bus, uint8_t address); +/** + * i2c_start_send_async: start an asynchronous 'send' transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * + * Return: 0 on success, -1 on error + */ +int i2c_start_send_async(I2CBus *bus, uint8_t address); + void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); +void i2c_ack(I2CBus *bus); +void i2c_bus_master(I2CBus *bus, QEMUBH *bh); +void i2c_bus_release(I2CBus *bus); int i2c_send(I2CBus *bus, uint8_t data); +int i2c_send_async(I2CBus *bus, uint8_t data); uint8_t i2c_recv(I2CBus *bus); bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, I2CNodeList *current_devs); diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 0f4d6b3fada..93f5d57c9dc 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -155,6 +155,7 @@ enum pmbus_registers { PMBUS_MFR_MAX_TEMP_1 = 0xC0, /* R/W word */ PMBUS_MFR_MAX_TEMP_2 = 0xC1, /* R/W word */ PMBUS_MFR_MAX_TEMP_3 = 0xC2, /* R/W word */ + PMBUS_IDLE_STATE = 0xFF, }; /* STATUS_WORD */ @@ -527,6 +528,12 @@ int pmbus_page_config(PMBusDevice *pmdev, uint8_t page_index, uint64_t flags); */ void pmbus_check_limits(PMBusDevice *pmdev); +/** + * Enter an idle state where only the PMBUS_ERR_BYTE will be returned + * indefinitely until a new command is issued. + */ +void pmbus_idle(PMBusDevice *pmdev); + extern const VMStateDescription vmstate_pmbus_device; #define VMSTATE_PMBUS_DEVICE(_field, _state) { \ diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h index e070f546e49..9fb3f8d7875 100644 --- a/include/hw/input/i8042.h +++ b/include/hw/input/i8042.h @@ -9,17 +9,85 @@ #define HW_INPUT_I8042_H #include "hw/isa/isa.h" +#include "hw/sysbus.h" +#include "hw/input/ps2.h" #include "qom/object.h" +#define I8042_KBD_IRQ 0 +#define I8042_MOUSE_IRQ 1 + +typedef struct KBDState { + uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ + uint8_t status; + uint8_t mode; + uint8_t outport; + uint32_t migration_flags; + uint32_t obsrc; + bool outport_present; + bool extended_state; + bool extended_state_loaded; + /* Bitmask of devices with data available. */ + uint8_t pending; + uint8_t obdata; + uint8_t cbdata; + uint8_t pending_tmp; + PS2KbdState ps2kbd; + PS2MouseState ps2mouse; + QEMUTimer *throttle_timer; + + qemu_irq irqs[2]; + qemu_irq a20_out; + hwaddr mask; +} KBDState; + +/* + * QEMU interface: + * + Named GPIO input "ps2-kbd-input-irq": set to 1 if the downstream PS2 + * keyboard device has asserted its irq + * + Named GPIO input "ps2-mouse-input-irq": set to 1 if the downstream PS2 + * mouse device has asserted its irq + * + Named GPIO output "a20": A20 line for x86 PCs + * + Unnamed GPIO output 0-1: i8042 output irqs for keyboard (0) or mouse (1) + */ + #define TYPE_I8042 "i8042" OBJECT_DECLARE_SIMPLE_TYPE(ISAKBDState, I8042) +struct ISAKBDState { + ISADevice parent_obj; + + KBDState kbd; + bool kbd_throttle; + MemoryRegion io[2]; + uint8_t kbd_irq; + uint8_t mouse_irq; +}; + +/* + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion defining the command/status/data + * registers (access determined by mask property and access type) + * + Named GPIO input "ps2-kbd-input-irq": set to 1 if the downstream PS2 + * keyboard device has asserted its irq + * + Named GPIO input "ps2-mouse-input-irq": set to 1 if the downstream PS2 + * mouse device has asserted its irq + * + Unnamed GPIO output 0-1: i8042 output irqs for keyboard (0) or mouse (1) + */ + +#define TYPE_I8042_MMIO "i8042-mmio" +OBJECT_DECLARE_SIMPLE_TYPE(MMIOKBDState, I8042_MMIO) + +struct MMIOKBDState { + SysBusDevice parent_obj; + + KBDState kbd; + uint32_t size; + MemoryRegion region; +}; + #define I8042_A20_LINE "a20" -void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, - MemoryRegion *region, ram_addr_t size, - hwaddr mask); void i8042_isa_mouse_fake_event(ISAKBDState *isa); void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out); diff --git a/include/hw/input/lasips2.h b/include/hw/input/lasips2.h index 0cd7b59064a..01911c50f9b 100644 --- a/include/hw/input/lasips2.h +++ b/include/hw/input/lasips2.h @@ -4,13 +4,77 @@ * Copyright (c) 2019 Sven Schnelle * */ + +/* + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion defining the LASI PS2 keyboard + * registers + * + sysbus MMIO region 1: MemoryRegion defining the LASI PS2 mouse + * registers + * + sysbus IRQ 0: LASI PS2 output irq + * + Named GPIO input "lasips2-port-input-irq[0..1]": set to 1 if the downstream + * LASIPS2Port has asserted its irq + */ + #ifndef HW_INPUT_LASIPS2_H #define HW_INPUT_LASIPS2_H #include "exec/hwaddr.h" +#include "hw/sysbus.h" +#include "hw/input/ps2.h" -#define TYPE_LASIPS2 "lasips2" +#define TYPE_LASIPS2_PORT "lasips2-port" +OBJECT_DECLARE_TYPE(LASIPS2Port, LASIPS2PortDeviceClass, LASIPS2_PORT) + +struct LASIPS2PortDeviceClass { + DeviceClass parent; + + DeviceRealize parent_realize; +}; + +typedef struct LASIPS2State LASIPS2State; + +struct LASIPS2Port { + DeviceState parent_obj; + + LASIPS2State *lasips2; + MemoryRegion reg; + PS2State *ps2dev; + uint8_t id; + uint8_t control; + uint8_t buf; + bool loopback_rbne; + qemu_irq irq; +}; -void lasips2_init(MemoryRegion *address_space, hwaddr base, qemu_irq irq); +#define TYPE_LASIPS2_KBD_PORT "lasips2-kbd-port" +OBJECT_DECLARE_SIMPLE_TYPE(LASIPS2KbdPort, LASIPS2_KBD_PORT) + +struct LASIPS2KbdPort { + LASIPS2Port parent_obj; + + PS2KbdState kbd; +}; + +#define TYPE_LASIPS2_MOUSE_PORT "lasips2-mouse-port" +OBJECT_DECLARE_SIMPLE_TYPE(LASIPS2MousePort, LASIPS2_MOUSE_PORT) + +struct LASIPS2MousePort { + LASIPS2Port parent_obj; + + PS2MouseState mouse; +}; + +struct LASIPS2State { + SysBusDevice parent_obj; + + LASIPS2KbdPort kbd_port; + LASIPS2MousePort mouse_port; + uint8_t int_status; + qemu_irq irq; +}; + +#define TYPE_LASIPS2 "lasips2" +OBJECT_DECLARE_SIMPLE_TYPE(LASIPS2State, LASIPS2) #endif /* HW_INPUT_LASIPS2_H */ diff --git a/include/hw/input/pl050.h b/include/hw/input/pl050.h new file mode 100644 index 00000000000..89ec4fafc9a --- /dev/null +++ b/include/hw/input/pl050.h @@ -0,0 +1,59 @@ +/* + * Arm PrimeCell PL050 Keyboard / Mouse Interface + * + * Copyright (c) 2006-2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#ifndef HW_PL050_H +#define HW_PL050_H + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "hw/input/ps2.h" +#include "hw/irq.h" + +struct PL050DeviceClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; +}; + +#define TYPE_PL050 "pl050" +OBJECT_DECLARE_TYPE(PL050State, PL050DeviceClass, PL050) + +struct PL050State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + PS2State *ps2dev; + uint32_t cr; + uint32_t clk; + uint32_t last; + int pending; + qemu_irq irq; + bool is_mouse; +}; + +#define TYPE_PL050_KBD_DEVICE "pl050_keyboard" +OBJECT_DECLARE_SIMPLE_TYPE(PL050KbdState, PL050_KBD_DEVICE) + +struct PL050KbdState { + PL050State parent_obj; + + PS2KbdState kbd; +}; + +#define TYPE_PL050_MOUSE_DEVICE "pl050_mouse" +OBJECT_DECLARE_SIMPLE_TYPE(PL050MouseState, PL050_MOUSE_DEVICE) + +struct PL050MouseState { + PL050State parent_obj; + + PS2MouseState mouse; +}; + +#endif diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index 35d983897a5..ff777582cd6 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -25,28 +25,89 @@ #ifndef HW_PS2_H #define HW_PS2_H +#include "hw/sysbus.h" + #define PS2_MOUSE_BUTTON_LEFT 0x01 #define PS2_MOUSE_BUTTON_RIGHT 0x02 #define PS2_MOUSE_BUTTON_MIDDLE 0x04 #define PS2_MOUSE_BUTTON_SIDE 0x08 #define PS2_MOUSE_BUTTON_EXTRA 0x10 -typedef struct PS2State PS2State; +struct PS2DeviceClass { + SysBusDeviceClass parent_class; + + DeviceReset parent_reset; +}; + +/* + * PS/2 buffer size. Keep 256 bytes for compatibility with + * older QEMU versions. + */ +#define PS2_BUFFER_SIZE 256 + +typedef struct { + uint8_t data[PS2_BUFFER_SIZE]; + int rptr, wptr, cwptr, count; +} PS2Queue; + +/* Output IRQ */ +#define PS2_DEVICE_IRQ 0 + +struct PS2State { + SysBusDevice parent_obj; + + PS2Queue queue; + int32_t write_cmd; + qemu_irq irq; +}; + +#define TYPE_PS2_DEVICE "ps2-device" +OBJECT_DECLARE_TYPE(PS2State, PS2DeviceClass, PS2_DEVICE) + +struct PS2KbdState { + PS2State parent_obj; + + int scan_enabled; + int translate; + int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ + int ledstate; + bool need_high_bit; + unsigned int modifiers; /* bitmask of MOD_* constants above */ +}; + +#define TYPE_PS2_KBD_DEVICE "ps2-kbd" +OBJECT_DECLARE_SIMPLE_TYPE(PS2KbdState, PS2_KBD_DEVICE) + +struct PS2MouseState { + PS2State parent_obj; + + uint8_t mouse_status; + uint8_t mouse_resolution; + uint8_t mouse_sample_rate; + uint8_t mouse_wrap; + uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ + uint8_t mouse_detect_state; + int mouse_dx; /* current values, needed for 'poll' mode */ + int mouse_dy; + int mouse_dz; + int mouse_dw; + uint8_t mouse_buttons; +}; + +#define TYPE_PS2_MOUSE_DEVICE "ps2-mouse" +OBJECT_DECLARE_SIMPLE_TYPE(PS2MouseState, PS2_MOUSE_DEVICE) /* ps2.c */ -void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); -void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); -void ps2_write_mouse(void *, int val); -void ps2_write_keyboard(void *, int val); +void ps2_write_mouse(PS2MouseState *s, int val); +void ps2_write_keyboard(PS2KbdState *s, int val); uint32_t ps2_read_data(PS2State *s); void ps2_queue_noirq(PS2State *s, int b); -void ps2_raise_irq(PS2State *s); void ps2_queue(PS2State *s, int b); void ps2_queue_2(PS2State *s, int b1, int b2); void ps2_queue_3(PS2State *s, int b1, int b2, int b3); void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4); -void ps2_keyboard_set_translation(void *opaque, int mode); -void ps2_mouse_fake_event(void *opaque); +void ps2_keyboard_set_translation(PS2KbdState *s, int mode); +void ps2_mouse_fake_event(PS2MouseState *s); int ps2_queue_empty(PS2State *s); #endif /* HW_PS2_H */ diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 996ed7ea93f..0ee48fca550 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -24,8 +24,9 @@ #define IOCSR_MAIL_SEND 0x48 #define IOCSR_ANY_SEND 0x158 -/* IPI system memory address */ -#define IPI_SYSTEM_MEM 0x1fe01000 +#define MAIL_SEND_ADDR (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND) +#define MAIL_SEND_OFFSET 0 +#define ANY_SEND_OFFSET (IOCSR_ANY_SEND - IOCSR_MAIL_SEND) #define MAX_IPI_CORE_NUM 4 #define MAX_IPI_MBX_NUM 4 @@ -46,7 +47,7 @@ typedef struct IPICore { struct LoongArchIPI { SysBusDevice parent_obj; MemoryRegion ipi_iocsr_mem[MAX_IPI_CORE_NUM]; - MemoryRegion ipi_system_mem[MAX_IPI_CORE_NUM]; + MemoryRegion ipi64_iocsr_mem[MAX_IPI_CORE_NUM]; }; #endif diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h index f668bfca7a5..6d67560dea3 100644 --- a/include/hw/intc/loongarch_pch_msi.h +++ b/include/hw/intc/loongarch_pch_msi.h @@ -17,4 +17,6 @@ struct LoongArchPCHMSI { SysBusDevice parent_obj; qemu_irq pch_msi_irq[PCH_MSI_IRQ_NUM]; MemoryRegion msi_mmio; + /* irq base passed to upper extioi intc */ + unsigned int irq_base; }; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 09a816191ca..f4f24df4283 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -17,6 +17,19 @@ #define LOONGARCH_ISA_IO_BASE 0x18000000UL #define LOONGARCH_ISA_IO_SIZE 0x0004000 +#define VIRT_FWCFG_BASE 0x1e020000UL +#define VIRT_BIOS_BASE 0x1c000000UL +#define VIRT_BIOS_SIZE (4 * MiB) + +#define VIRT_LOWMEM_BASE 0 +#define VIRT_LOWMEM_SIZE 0x10000000 +#define VIRT_HIGHMEM_BASE 0x90000000 +#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 LA_FDT_BASE 0x1c400000 +#define LA_FDT_SIZE 0x100000 struct LoongArchMachineState { /*< private >*/ @@ -26,8 +39,20 @@ struct LoongArchMachineState { MemoryRegion lowmem; MemoryRegion highmem; MemoryRegion isa_io; + MemoryRegion bios; + bool bios_loaded; + /* State for other subsystems/APIs: */ + FWCfgState *fw_cfg; + Notifier machine_done; + OnOffAuto acpi; + char *oem_id; + char *oem_table_id; + DeviceState *acpi_ged; + int fdt_size; }; #define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE) +bool loongarch_is_acpi_enabled(LoongArchMachineState *lams); +void loongarch_acpi_setup(LoongArchMachineState *lams); #endif diff --git a/include/hw/misc/aspeed_peci.h b/include/hw/misc/aspeed_peci.h new file mode 100644 index 00000000000..8382707d9fd --- /dev/null +++ b/include/hw/misc/aspeed_peci.h @@ -0,0 +1,29 @@ +/* + * Aspeed PECI Controller + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * This code is licensed under the GPL version 2 or later. See the COPYING + * file in the top-level directory. + */ + +#ifndef ASPEED_PECI_H +#define ASPEED_PECI_H + +#include "hw/sysbus.h" + +#define ASPEED_PECI_NR_REGS ((0xFC + 4) >> 2) +#define TYPE_ASPEED_PECI "aspeed.peci" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPECIState, ASPEED_PECI); + +struct AspeedPECIState { + /* */ + SysBusDevice parent; + + MemoryRegion mmio; + qemu_irq irq; + + uint32_t regs[ASPEED_PECI_NR_REGS]; +}; + +#endif diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h index 67e43b53ecc..405e6782b97 100644 --- a/include/hw/misc/aspeed_sbc.h +++ b/include/hw/misc/aspeed_sbc.h @@ -17,9 +17,22 @@ OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC) #define ASPEED_SBC_NR_REGS (0x93c >> 2) +#define QSR_AES BIT(27) +#define QSR_RSA1024 (0x0 << 12) +#define QSR_RSA2048 (0x1 << 12) +#define QSR_RSA3072 (0x2 << 12) +#define QSR_RSA4096 (0x3 << 12) +#define QSR_SHA224 (0x0 << 10) +#define QSR_SHA256 (0x1 << 10) +#define QSR_SHA384 (0x2 << 10) +#define QSR_SHA512 (0x3 << 10) + struct AspeedSBCState { SysBusDevice parent; + bool emmc_abr; + uint32_t signing_settings; + MemoryRegion iomem; uint32_t regs[ASPEED_SBC_NR_REGS]; diff --git a/include/hw/pci-host/i440fx.h b/include/hw/pci-host/i440fx.h index f068aaba8fd..52518dbf08e 100644 --- a/include/hw/pci-host/i440fx.h +++ b/include/hw/pci-host/i440fx.h @@ -36,7 +36,6 @@ struct PCII440FXState { #define TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE "igd-passthrough-i440FX" PCIBus *i440fx_init(const char *host_type, const char *pci_type, - PCII440FXState **pi440fx_state, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, ram_addr_t ram_size, diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index 08c5f78be24..0fdc86b9733 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -23,6 +23,9 @@ #define LS7A_PCI_IO_BASE 0x18004000UL #define LS7A_PCI_IO_SIZE 0xC000 +#define LS7A_PCI_MEM_BASE 0x40000000UL +#define LS7A_PCI_MEM_SIZE 0x40000000UL + #define LS7A_PCH_REG_BASE 0x10000000UL #define LS7A_IOAPIC_REG_BASE (LS7A_PCH_REG_BASE) #define LS7A_PCH_MSI_ADDR_LOW 0x2FF00000UL @@ -41,4 +44,5 @@ #define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000) #define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100) #define LS7A_RTC_LEN 0x100 +#define LS7A_SCI_IRQ (PCH_PIC_IRQ_OFFSET + 4) #endif diff --git a/include/hw/pci-host/pnv_phb3_regs.h b/include/hw/pci-host/pnv_phb3_regs.h index a174ef1f704..38f8ce9d740 100644 --- a/include/hw/pci-host/pnv_phb3_regs.h +++ b/include/hw/pci-host/pnv_phb3_regs.h @@ -12,22 +12,6 @@ #include "qemu/host-utils.h" -/* - * QEMU version of the GETFIELD/SETFIELD macros - * - * These are common with the PnvXive model. - */ -static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) -{ - return (word & mask) >> ctz64(mask); -} - -static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, - uint64_t value) -{ - return (word & ~mask) | ((value << ctz64(mask)) & mask); -} - /* * PBCQ XSCOM registers */ diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 898083b86f8..d5ddea558bf 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -238,6 +238,7 @@ #define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e #define PCI_DEVICE_ID_INTEL_82801D 0x24CD #define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_NVME 0x5845 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 86cb7d7f971..b9911942234 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -189,7 +189,8 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10, TYPE_PNV_CHIP_POWER10) PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir); -void pnv_phb_attach_root_port(PCIHostState *pci, const char *name); +void pnv_phb_attach_root_port(PCIHostState *pci, const char *name, + int index, int chip_id); #define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv") typedef struct PnvMachineClass PnvMachineClass; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 072dda2c726..530d739b1d9 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -164,6 +164,21 @@ struct SpaprMachineClass { SpaprIrq *irq; }; +#define WDT_MAX_WATCHDOGS 4 /* Maximum number of watchdog devices */ + +#define TYPE_SPAPR_WDT "spapr-wdt" +OBJECT_DECLARE_SIMPLE_TYPE(SpaprWatchdog, SPAPR_WDT) + +typedef struct SpaprWatchdog { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + QEMUTimer timer; + uint8_t action; /* One of PSERIES_WDTF_ACTION_xxx */ + uint8_t leave_others; /* leaveOtherWatchdogsRunningOnTimeout */ +} SpaprWatchdog; + /** * SpaprMachineState: */ @@ -264,6 +279,8 @@ struct SpaprMachineState { uint32_t FORM2_assoc_array[NUMA_NODES_MAX_NUM][FORM2_NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; + + SpaprWatchdog wds[WDT_MAX_WATCHDOGS]; }; #define H_SUCCESS 0 @@ -344,6 +361,7 @@ struct SpaprMachineState { #define H_P7 -60 #define H_P8 -61 #define H_P9 -62 +#define H_NOOP -63 #define H_UNSUPPORTED -67 #define H_OVERLAP -68 #define H_UNSUPPORTED_FLAG -256 @@ -564,8 +582,9 @@ struct SpaprMachineState { #define H_SCM_HEALTH 0x400 #define H_RPT_INVALIDATE 0x448 #define H_SCM_FLUSH 0x44C +#define H_WATCHDOG 0x45C -#define MAX_HCALL_OPCODE H_SCM_FLUSH +#define MAX_HCALL_OPCODE H_WATCHDOG /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -902,6 +921,7 @@ struct SpaprTceTable { bool bypass; bool need_vfio; bool skipping_replay; + bool def_win; int fd; MemoryRegion root; IOMMUMemoryRegion iommu; @@ -1027,6 +1047,7 @@ extern const VMStateDescription vmstate_spapr_cap_large_decr; extern const VMStateDescription vmstate_spapr_cap_ccf_assist; extern const VMStateDescription vmstate_spapr_cap_fwnmi; extern const VMStateDescription vmstate_spapr_cap_rpt_invalidate; +extern const VMStateDescription vmstate_spapr_wdt; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { @@ -1063,4 +1084,7 @@ target_ulong spapr_vof_client_architecture_support(MachineState *ms, target_ulong ovec_addr); void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt); +/* H_WATCHDOG */ +void spapr_watchdog_init(SpaprMachineState *spapr); + #endif /* HW_SPAPR_H */ diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h index 3a88e135d02..1330ca77de6 100644 --- a/include/hw/registerfields.h +++ b/include/hw/registerfields.h @@ -154,4 +154,74 @@ #define ARRAY_FIELD_DP64(regs, reg, field, val) \ (regs)[R_ ## reg] = FIELD_DP64((regs)[R_ ## reg], reg, field, val); + +/* + * These macros can be used for defining and extracting fields that have the + * same bit position across multiple registers. + */ + +/* Define shared SHIFT, LENGTH, and MASK constants */ +#define SHARED_FIELD(name, shift, length) \ + enum { name ## _ ## SHIFT = (shift)}; \ + enum { name ## _ ## LENGTH = (length)}; \ + enum { name ## _ ## MASK = MAKE_64BIT_MASK(shift, length)}; + +/* Extract a shared field */ +#define SHARED_FIELD_EX8(storage, field) \ + extract8((storage), field ## _SHIFT, field ## _LENGTH) + +#define SHARED_FIELD_EX16(storage, field) \ + extract16((storage), field ## _SHIFT, field ## _LENGTH) + +#define SHARED_FIELD_EX32(storage, field) \ + extract32((storage), field ## _SHIFT, field ## _LENGTH) + +#define SHARED_FIELD_EX64(storage, field) \ + extract64((storage), field ## _SHIFT, field ## _LENGTH) + +/* Extract a shared field from a register array */ +#define SHARED_ARRAY_FIELD_EX32(regs, offset, field) \ + SHARED_FIELD_EX32((regs)[(offset)], field) +#define SHARED_ARRAY_FIELD_EX64(regs, offset, field) \ + SHARED_FIELD_EX64((regs)[(offset)], field) + +/* Deposit a shared field */ +#define SHARED_FIELD_DP8(storage, field, val) ({ \ + struct { \ + unsigned int v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint8_t _d; \ + _d = deposit32((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +#define SHARED_FIELD_DP16(storage, field, val) ({ \ + struct { \ + unsigned int v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint16_t _d; \ + _d = deposit32((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +#define SHARED_FIELD_DP32(storage, field, val) ({ \ + struct { \ + unsigned int v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint32_t _d; \ + _d = deposit32((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +#define SHARED_FIELD_DP64(storage, field, val) ({ \ + struct { \ + uint64_t v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint64_t _d; \ + _d = deposit64((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +/* Deposit a shared field to a register array */ +#define SHARED_ARRAY_FIELD_DP32(regs, offset, field, val) \ + (regs)[(offset)] = SHARED_FIELD_DP32((regs)[(offset)], field, val); +#define SHARED_ARRAY_FIELD_DP64(regs, offset, field, val) \ + (regs)[(offset)] = SHARED_FIELD_DP64((regs)[(offset)], field, val); + #endif diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 1ffb367f94f..e284e3a4ec0 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -226,4 +226,10 @@ SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int target, int lun); /* scsi-generic.c. */ extern const SCSIReqOps scsi_generic_req_ops; +/* scsi-disk.c */ +#define SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR 0 +#define SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD 1 +#define SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE 2 +#define SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED 3 + #endif diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h index 3e47ff7e48d..aa2c2767df9 100644 --- a/include/hw/sensor/isl_pmbus_vr.h +++ b/include/hw/sensor/isl_pmbus_vr.h @@ -12,12 +12,17 @@ #include "hw/i2c/pmbus_device.h" #include "qom/object.h" +#define TYPE_ISL69259 "isl69259" #define TYPE_ISL69260 "isl69260" #define TYPE_RAA228000 "raa228000" #define TYPE_RAA229004 "raa229004" +#define ISL_MAX_IC_DEVICE_ID_LEN 16 struct ISLState { PMBusDevice parent; + + uint8_t ic_device_id[ISL_MAX_IC_DEVICE_ID_LEN]; + uint8_t ic_device_id_len; }; OBJECT_DECLARE_SIMPLE_TYPE(ISLState, ISL69260) diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 81bf3109f83..eab46d7f0b3 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -69,6 +69,8 @@ typedef int (*vhost_set_vring_kick_op)(struct vhost_dev *dev, struct vhost_vring_file *file); typedef int (*vhost_set_vring_call_op)(struct vhost_dev *dev, struct vhost_vring_file *file); +typedef int (*vhost_set_vring_err_op)(struct vhost_dev *dev, + struct vhost_vring_file *file); typedef int (*vhost_set_vring_busyloop_timeout_op)(struct vhost_dev *dev, struct vhost_vring_state *r); typedef int (*vhost_set_features_op)(struct vhost_dev *dev, @@ -145,6 +147,7 @@ typedef struct VhostOps { vhost_get_vring_base_op vhost_get_vring_base; vhost_set_vring_kick_op vhost_set_vring_kick; vhost_set_vring_call_op vhost_set_vring_call; + vhost_set_vring_err_op vhost_set_vring_err; vhost_set_vring_busyloop_timeout_op vhost_set_vring_busyloop_timeout; vhost_set_features_op vhost_set_features; vhost_get_features_op vhost_get_features; diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index a29dbb3f537..d10a89303e4 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -15,6 +15,7 @@ #include #include "hw/virtio/vhost-iova-tree.h" +#include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/virtio.h" #include "standard-headers/linux/vhost_types.h" @@ -34,9 +35,16 @@ typedef struct vhost_vdpa { bool shadow_vqs_enabled; /* IOVA mapping used by the Shadow Virtqueue */ VhostIOVATree *iova_tree; + Error *migration_blocker; GPtrArray *shadow_vqs; + const VhostShadowVirtqueueOps *shadow_vq_ops; + void *shadow_vq_ops_opaque; struct vhost_dev *dev; VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX]; } VhostVDPA; +int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, + void *vaddr, bool readonly); +int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, hwaddr size); + #endif diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index b291fe4e24f..a346f23d13c 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -29,6 +29,7 @@ struct vhost_virtqueue { unsigned long long used_phys; unsigned used_size; EventNotifier masked_notifier; + EventNotifier error_notifier; struct vhost_dev *dev; }; @@ -246,8 +247,29 @@ bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n); */ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, bool mask); + +/** + * vhost_get_features() - return a sanitised set of feature bits + * @hdev: common vhost_dev structure + * @feature_bits: pointer to terminated table of feature bits + * @features: original feature set + * + * This returns a set of features bits that is an intersection of what + * is supported by the vhost backend (hdev->features), the supported + * feature_bits and the requested feature set. + */ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, uint64_t features); + +/** + * vhost_ack_features() - set vhost acked_features + * @hdev: common vhost_dev structure + * @feature_bits: pointer to terminated table of feature bits + * @features: requested feature set + * + * This sets the internal hdev->acked_features to the intersection of + * the backends advertised features and the supported feature_bits. + */ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, uint64_t features); bool vhost_has_free_slot(void); diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h index a2228d7b2eb..348749f5d5c 100644 --- a/include/hw/virtio/virtio-crypto.h +++ b/include/hw/virtio/virtio-crypto.h @@ -50,6 +50,7 @@ typedef struct VirtIOCryptoConf { uint32_t mac_algo_l; uint32_t mac_algo_h; uint32_t aead_algo; + uint32_t akcipher_algo; /* Maximum length of cipher key */ uint32_t max_cipher_key_len; @@ -71,9 +72,7 @@ typedef struct VirtIOCryptoReq { size_t in_len; VirtQueue *vq; struct VirtIOCrypto *vcrypto; - union { - CryptoDevBackendSymOpInfo *sym_op_info; - } u; + CryptoDevBackendOpInfo op_info; } VirtIOCryptoReq; typedef struct VirtIOCryptoQueue { diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 84391f84482..2ad5ee320be 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -37,6 +37,8 @@ typedef struct IOMMUDevice { int devfn; IOMMUMemoryRegion iommu_mr; AddressSpace as; + MemoryRegion root; /* The root container of the device */ + MemoryRegion bypass_mr; /* The alias of shared memory MR */ } IOMMUDevice; typedef struct IOMMUPciBus { @@ -56,7 +58,7 @@ struct VirtIOIOMMU { ReservedRegion *reserved_regions; uint32_t nb_reserved_regions; GTree *domains; - QemuMutex mutex; + QemuRecMutex mutex; GTree *endpoints; bool boot_bypass; }; diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index eb87032627d..ef234ffe7ef 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -35,6 +35,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET) * and latency. */ #define TX_BURST 256 +/* Maximum VIRTIO_NET_CTRL_MAC_TABLE_SET unicast + multicast entries. */ +#define MAC_TABLE_ENTRIES 64 + typedef struct virtio_net_conf { uint32_t txtimer; @@ -218,6 +221,10 @@ struct VirtIONet { struct EBPFRSSContext ebpf_rss; }; +size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev, + const struct iovec *in_sg, unsigned in_num, + const struct iovec *out_sg, + unsigned out_num); void virtio_net_set_netclient_name(VirtIONet *n, const char *name, const char *type); diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 0f9962b1c19..afdf9c436af 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -21,8 +21,8 @@ extern enum xen_mode xen_mode; extern bool xen_domid_restrict; int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); +int xen_set_pci_link_route(uint8_t link, uint8_t irq); void xen_piix3_set_irq(void *opaque, int irq_num, int level); -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); void xen_hvm_inject_msi(uint64_t addr, uint32_t data); int xen_is_pirq_msi(uint32_t msi_data); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 179741ff791..77ce17d8a4f 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -316,12 +316,6 @@ static inline int xen_set_pci_intx_level(domid_t domid, uint16_t segment, device, intx, level); } -static inline int xen_set_pci_link_route(domid_t domid, uint8_t link, - uint8_t irq) -{ - return xendevicemodel_set_pci_link_route(xen_dmod, domid, link, irq); -} - static inline int xen_inject_msi(domid_t domid, uint64_t msi_addr, uint32_t msi_data) { diff --git a/include/io/channel-null.h b/include/io/channel-null.h new file mode 100644 index 00000000000..f6d54e63cf9 --- /dev/null +++ b/include/io/channel-null.h @@ -0,0 +1,55 @@ +/* + * QEMU I/O channels null driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef QIO_CHANNEL_FILE_H +#define QIO_CHANNEL_FILE_H + +#include "io/channel.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL_NULL "qio-channel-null" +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelNull, QIO_CHANNEL_NULL) + + +/** + * QIOChannelNull: + * + * The QIOChannelNull object provides a channel implementation + * that discards all writes and returns EOF for all reads. + */ + +struct QIOChannelNull { + QIOChannel parent; + bool closed; +}; + + +/** + * qio_channel_null_new: + * + * Create a new IO channel object that discards all writes + * and returns EOF for all reads. + * + * Returns: the new channel object + */ +QIOChannelNull * +qio_channel_null_new(void); + +#endif /* QIO_CHANNEL_NULL_H */ diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 96d014826ad..a618eb1e4ec 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -131,7 +131,11 @@ void hmp_replay_delete_break(Monitor *mon, const QDict *qdict); void hmp_replay_seek(Monitor *mon, const QDict *qdict); void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict); void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict); +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); void hmp_human_readable_text_helper(Monitor *mon, HumanReadableText *(*qmp_handler)(Error **)); +void hmp_info_stats(Monitor *mon, const QDict *qdict); #endif diff --git a/include/monitor/stats.h b/include/monitor/stats.h new file mode 100644 index 00000000000..fcf09831542 --- /dev/null +++ b/include/monitor/stats.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef STATS_H +#define STATS_H + +#include "qapi/qapi-types-stats.h" + +typedef void StatRetrieveFunc(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp); +typedef void SchemaRetrieveFunc(StatsSchemaList **result, Error **errp); + +/* + * Register callbacks for the QMP query-stats command. + * + * @provider: stats provider checked against QMP command arguments + * @stats_fn: routine to query stats: + * @schema_fn: routine to query stat schemas: + */ +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn); + +/* + * Helper routines for adding stats entries to the results lists. + */ +void add_stats_entry(StatsResultList **, StatsProvider, const char *id, + StatsList *stats_list); +void add_stats_schema(StatsSchemaList **, StatsProvider, StatsTarget, + StatsSchemaValueList *); + +/* + * True if a string matches the filter passed to the stats_fn callabck, + * false otherwise. + * + * Note that an empty list means no filtering, i.e. all strings will + * return true. + */ +bool apply_str_list_filter(const char *string, strList *list); + +#endif /* STATS_H */ diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 4f4c283f6fc..be56da1b999 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -68,6 +68,7 @@ typedef struct AccelClass { AccelClass *accel_find(const char *opt_name); AccelState *current_accel(void); +const char *current_accel_name(void); void accel_init_interfaces(AccelClass *ac); diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index d1548d5b11b..08c5bb3c766 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -331,6 +331,19 @@ static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) qemu_co_sleep_ns_wakeable(&w, type, ns); } +typedef void CleanupFunc(void *opaque); +/** + * Run entry in a coroutine and start timer. Wait for entry to finish or for + * timer to elapse, what happen first. If entry finished, return 0, if timer + * elapsed earlier, return -ETIMEDOUT. + * + * Be careful, entry execution is not canceled, user should handle it somehow. + * If @clean is provided, it's called after coroutine finish if timeout + * happened. + */ +int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, + uint64_t timeout_ns, CleanupFunc clean); + /** * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be * deleted. @sleep_state must be the variable whose address was given to diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 40e10e19a7e..92c436d8c70 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -1,6 +1,24 @@ #ifndef QEMU_CUTILS_H #define QEMU_CUTILS_H +/* + * si_prefix: + * @exp10: exponent of 10, a multiple of 3 between -18 and 18 inclusive. + * + * Return a SI prefix (n, u, m, K, M, etc.) corresponding + * to the given exponent of 10. + */ +const char *si_prefix(unsigned int exp10); + +/* + * iec_binary_prefix: + * @exp2: exponent of 2, a multiple of 10 between 0 and 60 inclusive. + * + * Return an IEC binary prefix (Ki, Mi, etc.) corresponding + * to the given exponent of 2. + */ +const char *iec_binary_prefix(unsigned int exp2); + /** * pstrcpy: * @buf: buffer to copy string into @@ -206,9 +224,21 @@ const char *qemu_get_exec_dir(void); * @dir: the directory (typically a `CONFIG_*DIR` variable) to be relocated. * * Returns a path for @dir that uses the directory of the running executable - * as the prefix. For example, if `bindir` is `/usr/bin` and @dir is - * `/usr/share/qemu`, the function will append `../share/qemu` to the - * directory that contains the running executable and return the result. + * as the prefix. + * + * When a directory named `qemu-bundle` exists in the directory of the running + * executable, the path to the directory will be prepended to @dir. For + * example, if the directory of the running executable is `/qemu/build` @dir + * is `/usr/share/qemu`, the result will be + * `/qemu/build/qemu-bundle/usr/share/qemu`. The directory is expected to exist + * in the build tree. + * + * Otherwise, the directory of the running executable will be used as the + * prefix and it appends the relative path from `bindir` to @dir. For example, + * if the directory of the running executable is `/opt/qemu/bin`, `bindir` is + * `/usr/bin` and @dir is `/usr/share/qemu`, the result will be + * `/opt/qemu/bin/../share/qemu`. + * * The returned string should be freed by the caller. */ char *get_relocated_path(const char *dir); diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 5bd986aa44a..af4e4ab7468 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -76,20 +76,9 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size); * * Store result of merging @a and @b into @result. * @result is allowed to be equal to @a or @b. - * - * Return true if the merge was successful, - * false if it was not attempted. - */ -bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); - -/** - * hbitmap_can_merge: - * - * hbitmap_can_merge(a, b) && hbitmap_can_merge(a, result) is sufficient and - * necessary for hbitmap_merge will not fail. - * + * All bitmaps must have same size. */ -bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b); +void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); /** * hbitmap_empty: diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index f19bd291056..29f3a998788 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -32,6 +32,7 @@ #include "qemu/compiler.h" #include "qemu/bswap.h" +#include "qemu/int128.h" #ifdef CONFIG_INT128 static inline void mulu64(uint64_t *plow, uint64_t *phigh, @@ -375,12 +376,7 @@ static inline uint64_t uabs64(int64_t v) */ static inline bool sadd32_overflow(int32_t x, int32_t y, int32_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return ((*ret ^ x) & ~(x ^ y)) < 0; -#endif } /** @@ -393,12 +389,7 @@ static inline bool sadd32_overflow(int32_t x, int32_t y, int32_t *ret) */ static inline bool sadd64_overflow(int64_t x, int64_t y, int64_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return ((*ret ^ x) & ~(x ^ y)) < 0; -#endif } /** @@ -411,12 +402,7 @@ static inline bool sadd64_overflow(int64_t x, int64_t y, int64_t *ret) */ static inline bool uadd32_overflow(uint32_t x, uint32_t y, uint32_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return *ret < x; -#endif } /** @@ -429,12 +415,7 @@ static inline bool uadd32_overflow(uint32_t x, uint32_t y, uint32_t *ret) */ static inline bool uadd64_overflow(uint64_t x, uint64_t y, uint64_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return *ret < x; -#endif } /** @@ -448,12 +429,7 @@ static inline bool uadd64_overflow(uint64_t x, uint64_t y, uint64_t *ret) */ static inline bool ssub32_overflow(int32_t x, int32_t y, int32_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return ((*ret ^ x) & (x ^ y)) < 0; -#endif } /** @@ -467,12 +443,7 @@ static inline bool ssub32_overflow(int32_t x, int32_t y, int32_t *ret) */ static inline bool ssub64_overflow(int64_t x, int64_t y, int64_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return ((*ret ^ x) & (x ^ y)) < 0; -#endif } /** @@ -486,12 +457,7 @@ static inline bool ssub64_overflow(int64_t x, int64_t y, int64_t *ret) */ static inline bool usub32_overflow(uint32_t x, uint32_t y, uint32_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return x < y; -#endif } /** @@ -505,12 +471,7 @@ static inline bool usub32_overflow(uint32_t x, uint32_t y, uint32_t *ret) */ static inline bool usub64_overflow(uint64_t x, uint64_t y, uint64_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return x < y; -#endif } /** @@ -523,13 +484,7 @@ static inline bool usub64_overflow(uint64_t x, uint64_t y, uint64_t *ret) */ static inline bool smul32_overflow(int32_t x, int32_t y, int32_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - int64_t z = (int64_t)x * y; - *ret = z; - return *ret != z; -#endif } /** @@ -542,14 +497,7 @@ static inline bool smul32_overflow(int32_t x, int32_t y, int32_t *ret) */ static inline bool smul64_overflow(int64_t x, int64_t y, int64_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - uint64_t hi, lo; - muls64(&lo, &hi, x, y); - *ret = lo; - return hi != ((int64_t)lo >> 63); -#endif } /** @@ -562,13 +510,7 @@ static inline bool smul64_overflow(int64_t x, int64_t y, int64_t *ret) */ static inline bool umul32_overflow(uint32_t x, uint32_t y, uint32_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - uint64_t z = (uint64_t)x * y; - *ret = z; - return z > UINT32_MAX; -#endif } /** @@ -581,13 +523,7 @@ static inline bool umul32_overflow(uint32_t x, uint32_t y, uint32_t *ret) */ static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - uint64_t hi; - mulu64(ret, &hi, x, y); - return hi != 0; -#endif } /* @@ -849,4 +785,6 @@ static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, #endif } +Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); +Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); #endif diff --git a/include/qemu/int128.h b/include/qemu/int128.h index ef71f56e3f5..d2b76ca6acd 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -128,11 +128,21 @@ static inline bool int128_ge(Int128 a, Int128 b) return a >= b; } +static inline bool int128_uge(Int128 a, Int128 b) +{ + return ((__uint128_t)a) >= ((__uint128_t)b); +} + static inline bool int128_lt(Int128 a, Int128 b) { return a < b; } +static inline bool int128_ult(Int128 a, Int128 b) +{ + return (__uint128_t)a < (__uint128_t)b; +} + static inline bool int128_le(Int128 a, Int128 b) { return a <= b; @@ -177,6 +187,15 @@ static inline Int128 bswap128(Int128 a) #endif } +static inline int clz128(Int128 a) +{ + if (a >> 64) { + return __builtin_clzll(a >> 64); + } else { + return (a) ? __builtin_clzll((uint64_t)a) + 64 : 128; + } +} + static inline Int128 int128_divu(Int128 a, Int128 b) { return (__uint128_t)a / (__uint128_t)b; @@ -373,11 +392,21 @@ static inline bool int128_ge(Int128 a, Int128 b) return a.hi > b.hi || (a.hi == b.hi && a.lo >= b.lo); } +static inline bool int128_uge(Int128 a, Int128 b) +{ + return (uint64_t)a.hi > (uint64_t)b.hi || (a.hi == b.hi && a.lo >= b.lo); +} + static inline bool int128_lt(Int128 a, Int128 b) { return !int128_ge(a, b); } +static inline bool int128_ult(Int128 a, Int128 b) +{ + return !int128_uge(a, b); +} + static inline bool int128_le(Int128 a, Int128 b) { return int128_ge(b, a); @@ -418,6 +447,15 @@ static inline Int128 bswap128(Int128 a) return int128_make128(bswap64(a.hi), bswap64(a.lo)); } +static inline int clz128(Int128 a) +{ + if (a.hi) { + return __builtin_clzll(a.hi); + } else { + return (a.lo) ? __builtin_clzll(a.lo) + 64 : 128; + } +} + Int128 int128_divu(Int128, Int128); Int128 int128_remu(Int128, Int128); Int128 int128_divs(Int128, Int128); diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index c938fb07933..16bbfdf5f8d 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -72,10 +72,8 @@ int iova_tree_insert(IOVATree *tree, const DMAMap *map); * provided. The range does not need to be exactly what has inserted, * all the mappings that are included in the provided range will be * removed from the tree. Here map->translated_addr is meaningless. - * - * Return: 0 if succeeded, or <0 if error. */ -int iova_tree_remove(IOVATree *tree, const DMAMap *map); +void iova_tree_remove(IOVATree *tree, const DMAMap *map); /** * iova_tree_find: diff --git a/include/qom/object.h b/include/qom/object.h index 5f3d5b5bf53..ef7258a5e14 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -16,7 +16,6 @@ #include "qapi/qapi-builtin-types.h" #include "qemu/module.h" -#include "qom/object.h" struct TypeImpl; typedef struct TypeImpl *Type; diff --git a/include/scsi/constants.h b/include/scsi/constants.h index 2a32c08b5e9..6a8bad556a4 100644 --- a/include/scsi/constants.h +++ b/include/scsi/constants.h @@ -225,6 +225,7 @@ #define TYPE_NO_LUN 0x7f /* Mode page codes for mode sense/set */ +#define MODE_PAGE_VENDOR_SPECIFIC 0x00 #define MODE_PAGE_R_W_ERROR 0x01 #define MODE_PAGE_HD_GEOMETRY 0x04 #define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05 @@ -234,6 +235,7 @@ #define MODE_PAGE_FAULT_FAIL 0x1c #define MODE_PAGE_TO_PROTECT 0x1d #define MODE_PAGE_CAPABILITIES 0x2a +#define MODE_PAGE_APPLE_VENDOR 0x30 #define MODE_PAGE_ALLS 0x3f /* Not in Mt. Fuji, but in ATAPI 2.6 -- deprecated now in favor * of MODE_PAGE_SENSE_POWER */ diff --git a/semihosting/common-semi.h b/include/semihosting/common-semi.h similarity index 96% rename from semihosting/common-semi.h rename to include/semihosting/common-semi.h index 0bfab1c669b..0a91db7c414 100644 --- a/semihosting/common-semi.h +++ b/include/semihosting/common-semi.h @@ -34,6 +34,6 @@ #ifndef COMMON_SEMI_H #define COMMON_SEMI_H -target_ulong do_common_semihosting(CPUState *cs); +void do_common_semihosting(CPUState *cs); #endif /* COMMON_SEMI_H */ diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 0238f540f4b..bd78e5f03fc 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -12,58 +12,48 @@ #include "cpu.h" /** - * qemu_semihosting_console_outs: - * @env: CPUArchState - * @s: host address of null terminated guest string + * qemu_semihosting_console_read: + * @cs: CPUState + * @buf: host buffer + * @len: buffer size * - * Send a null terminated guest string to the debug console. This may - * be the remote gdb session if a softmmu guest is currently being - * debugged. + * Receive at least one character from debug console. As this call may + * block if no data is available we suspend the CPU and will re-execute the + * instruction when data is there. Therefore two conditions must be met: * - * Returns: number of bytes written. + * - CPUState is synchronized before calling this function + * - pc is only updated once the character is successfully returned + * + * Returns: number of characters read, OR cpu_loop_exit! */ -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s); +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len); /** - * qemu_semihosting_console_outc: - * @env: CPUArchState - * @s: host address of null terminated guest string + * qemu_semihosting_console_write: + * @buf: host buffer + * @len: buffer size * - * Send single character from guest memory to the debug console. This - * may be the remote gdb session if a softmmu guest is currently being - * debugged. + * Write len bytes from buf to the debug console. * - * Returns: nothing + * Returns: number of bytes written -- this should only ever be short + * on some sort of i/o error. */ -void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); +int qemu_semihosting_console_write(void *buf, int len); -/** - * qemu_semihosting_console_inc: - * @env: CPUArchState - * - * Receive single character from debug console. This may be the remote - * gdb session if a softmmu guest is currently being debugged. As this - * call may block if no data is available we suspend the CPU and will - * re-execute the instruction when data is there. Therefore two - * conditions must be met: - * - CPUState is synchronized before calling this function - * - pc is only updated once the character is successfully returned +/* + * qemu_semihosting_console_block_until_ready: + * @cs: CPUState * - * Returns: character read OR cpu_loop_exit! + * If no data is available we suspend the CPU and will re-execute the + * instruction when data is available. */ -target_ulong qemu_semihosting_console_inc(CPUArchState *env); +void qemu_semihosting_console_block_until_ready(CPUState *cs); /** - * qemu_semihosting_log_out: - * @s: pointer to string - * @len: length of string - * - * Send a string to the debug output. Unlike console_out these strings - * can't be sent to a remote gdb instance as they don't exist in guest - * memory. + * qemu_semihosting_console_ready: * - * Returns: number of bytes written + * Return true if characters are available for read; does not block. */ -int qemu_semihosting_log_out(const char *s, int len); +bool qemu_semihosting_console_ready(void); #endif /* SEMIHOST_CONSOLE_H */ diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h new file mode 100644 index 00000000000..3d426fedab3 --- /dev/null +++ b/include/semihosting/guestfd.h @@ -0,0 +1,91 @@ +/* + * Hosted file support for semihosting syscalls. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMIHOSTING_GUESTFD_H +#define SEMIHOSTING_GUESTFD_H + +typedef enum GuestFDType { + GuestFDUnused = 0, + GuestFDHost, + GuestFDGDB, + GuestFDStatic, + GuestFDConsole, +} GuestFDType; + +/* + * Guest file descriptors are integer indexes into an array of + * these structures (we will dynamically resize as necessary). + */ +typedef struct GuestFD { + GuestFDType type; + union { + int hostfd; + struct { + const uint8_t *data; + size_t len; + size_t off; + } staticfile; + }; +} GuestFD; + +/* + * For ARM semihosting, we have a separate structure for routing + * data for the console which is outside the guest fd address space. + */ +extern GuestFD console_in_gf; +extern GuestFD console_out_gf; + +/** + * alloc_guestfd: + * + * Allocate an unused GuestFD index. The associated guestfd index + * will still be GuestFDUnused until it is initialized. + */ +int alloc_guestfd(void); + +/** + * dealloc_guestfd: + * @guestfd: GuestFD index + * + * Deallocate a GuestFD index. The associated GuestFD structure + * will be recycled for a subsequent allocation. + */ +void dealloc_guestfd(int guestfd); + +/** + * get_guestfd: + * @guestfd: GuestFD index + * + * Return the GuestFD structure associated with an initialized @guestfd, + * or NULL if it has not been allocated, or hasn't been initialized. + */ +GuestFD *get_guestfd(int guestfd); + +/** + * associate_guestfd: + * @guestfd: GuestFD index + * @hostfd: host file descriptor + * + * Initialize the GuestFD for @guestfd to GuestFDHost using @hostfd. + */ +void associate_guestfd(int guestfd, int hostfd); + +/** + * staticfile_guestfd: + * @guestfd: GuestFD index + * @data: data to be read + * @len: length of @data + * + * Initialize the GuestFD for @guestfd to GuestFDStatic. + * The @len bytes at @data will be returned to the guest on reads. + */ +void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len); + +#endif /* SEMIHOSTING_GUESTFD_H */ diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index 0c55ade3ac1..93a3c21b44d 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -51,14 +51,6 @@ static inline const char *semihosting_get_cmdline(void) { return NULL; } - -static inline Chardev *semihosting_get_chardev(void) -{ - return NULL; -} -static inline void qemu_semihosting_console_init(void) -{ -} #else /* !CONFIG_USER_ONLY */ bool semihosting_enabled(void); SemihostingTarget semihosting_get_target(void); @@ -66,12 +58,12 @@ const char *semihosting_get_arg(int i); int semihosting_get_argc(void); const char *semihosting_get_cmdline(void); void semihosting_arg_fallback(const char *file, const char *cmd); -Chardev *semihosting_get_chardev(void); /* for vl.c hooks */ void qemu_semihosting_enable(void); int qemu_semihosting_config_options(const char *opt); -void qemu_semihosting_connect_chardevs(void); -void qemu_semihosting_console_init(void); +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 new file mode 100644 index 00000000000..4f08dfc0986 --- /dev/null +++ b/include/semihosting/softmmu-uaccess.h @@ -0,0 +1,59 @@ +/* + * 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 new file mode 100644 index 00000000000..3a5ec229ebf --- /dev/null +++ b/include/semihosting/syscalls.h @@ -0,0 +1,75 @@ +/* + * Syscall implementations for semihosting. + * + * Copyright (c) 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMIHOSTING_SYSCALLS_H +#define SEMIHOSTING_SYSCALLS_H + +/* + * Argument loading from the guest is performed by the caller; + * results are returned via the 'complete' callback. + * + * String operands are in address/len pairs. The len argument may be 0 + * (when the semihosting abi does not already provide the length), + * or non-zero (where it should include the terminating zero). + */ + +typedef struct GuestFD GuestFD; + +void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode); + +void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, + int fd); + +void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len); + +void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len); + +void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len); + +void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len); + +void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, int64_t off, int gdb_whence); + +void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + int fd); + +void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, + gdb_syscall_complete_cb flen_cb, + int fd, target_ulong fstat_addr); + +void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong addr); + +void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr); + +void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len); + +void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len); + +void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len); + +void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr); + +void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, GIOCondition cond, int timeout); + +#endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/include/standard-headers/asm-m68k/bootinfo-virt.h b/include/standard-headers/asm-m68k/bootinfo-virt.h index 81be1e09249..1b1ffd4705d 100644 --- a/include/standard-headers/asm-m68k/bootinfo-virt.h +++ b/include/standard-headers/asm-m68k/bootinfo-virt.h @@ -12,6 +12,7 @@ #define BI_VIRT_GF_TTY_BASE 0x8003 #define BI_VIRT_VIRTIO_BASE 0x8004 #define BI_VIRT_CTRL_BASE 0x8005 +#define BI_VIRT_RNG_SEED 0x8006 #define VIRT_BOOTI_VERSION MK_BI_VERSION(2, 0) diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h index 6517c392952..50f5aa2e07d 100644 --- a/include/sysemu/block-backend-io.h +++ b/include/sysemu/block-backend-io.h @@ -72,7 +72,6 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_get_max_iov(BlockBackend *blk); int blk_get_max_hw_iov(BlockBackend *blk); -void blk_set_guest_block_size(BlockBackend *blk, int align); void blk_io_plug(BlockBackend *blk); void blk_io_unplug(BlockBackend *blk); @@ -102,60 +101,79 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, * the "I/O or GS" API. */ -int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes); -int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, - BdrvRequestFlags flags); +int generated_co_wrapper blk_pread(BlockBackend *blk, int64_t offset, + int64_t bytes, void *buf, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, + void *buf, BdrvRequestFlags flags); + +int generated_co_wrapper blk_preadv(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, + +int generated_co_wrapper blk_preadv_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, + BdrvRequestFlags flags); +int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, BdrvRequestFlags flags); + +int generated_co_wrapper blk_pwrite(BlockBackend *blk, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, + const void *buf, BdrvRequestFlags flags); + +int generated_co_wrapper blk_pwritev(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -static inline int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); - - assert(bytes <= SIZE_MAX); - - return blk_co_preadv(blk, offset, bytes, &qiov, flags); -} - -static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); +int generated_co_wrapper blk_pwritev_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, + int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); - assert(bytes <= SIZE_MAX); +int generated_co_wrapper blk_pwrite_compressed(BlockBackend *blk, + int64_t offset, int64_t bytes, + const void *buf); +int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, + int64_t bytes, const void *buf); - return blk_co_pwritev(blk, offset, bytes, &qiov, flags); -} +int generated_co_wrapper blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, BdrvRequestFlags flags); +int generated_co_wrapper blk_pdiscard(BlockBackend *blk, int64_t offset, + int64_t bytes); int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); +int generated_co_wrapper blk_flush(BlockBackend *blk); int coroutine_fn blk_co_flush(BlockBackend *blk); -int blk_flush(BlockBackend *blk); - -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); -int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int64_t bytes); -int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); -int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); +int generated_co_wrapper blk_ioctl(BlockBackend *blk, unsigned long int req, + void *buf); +int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, + void *buf); + +int generated_co_wrapper blk_truncate(BlockBackend *blk, int64_t offset, + bool exact, PreallocMode prealloc, + BdrvRequestFlags flags, Error **errp); +int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp); #endif /* BLOCK_BACKEND_IO_H */ diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h index f4d4057d4da..37c3a360fd5 100644 --- a/include/sysemu/cryptodev.h +++ b/include/sysemu/cryptodev.h @@ -50,13 +50,13 @@ typedef struct CryptoDevBackendClient enum CryptoDevBackendAlgType { CRYPTODEV_BACKEND_ALG_SYM, + CRYPTODEV_BACKEND_ALG_ASYM, CRYPTODEV_BACKEND_ALG__MAX, }; /** * CryptoDevBackendSymSessionInfo: * - * @op_code: operation code (refer to virtio_crypto.h) * @cipher_alg: algorithm type of CIPHER * @key_len: byte length of cipher key * @hash_alg: algorithm type of HASH/MAC @@ -74,7 +74,6 @@ enum CryptoDevBackendAlgType { */ typedef struct CryptoDevBackendSymSessionInfo { /* corresponding with virtio crypto spec */ - uint32_t op_code; uint32_t cipher_alg; uint32_t key_len; uint32_t hash_alg; @@ -89,11 +88,36 @@ typedef struct CryptoDevBackendSymSessionInfo { uint8_t *auth_key; } CryptoDevBackendSymSessionInfo; +/** + * CryptoDevBackendAsymSessionInfo: + */ +typedef struct CryptoDevBackendRsaPara { + uint32_t padding_algo; + uint32_t hash_algo; +} CryptoDevBackendRsaPara; + +typedef struct CryptoDevBackendAsymSessionInfo { + /* corresponding with virtio crypto spec */ + uint32_t algo; + uint32_t keytype; + uint32_t keylen; + uint8_t *key; + union { + CryptoDevBackendRsaPara rsa; + } u; +} CryptoDevBackendAsymSessionInfo; + +typedef struct CryptoDevBackendSessionInfo { + uint32_t op_code; + union { + CryptoDevBackendSymSessionInfo sym_sess_info; + CryptoDevBackendAsymSessionInfo asym_sess_info; + } u; +} CryptoDevBackendSessionInfo; + /** * CryptoDevBackendSymOpInfo: * - * @session_id: session index which was previously - * created by cryptodev_backend_sym_create_session() * @aad_len: byte length of additional authenticated data * @iv_len: byte length of initialization vector or counter * @src_len: byte length of source data @@ -119,7 +143,6 @@ typedef struct CryptoDevBackendSymSessionInfo { * */ typedef struct CryptoDevBackendSymOpInfo { - uint64_t session_id; uint32_t aad_len; uint32_t iv_len; uint32_t src_len; @@ -138,6 +161,33 @@ typedef struct CryptoDevBackendSymOpInfo { uint8_t data[]; } CryptoDevBackendSymOpInfo; + +/** + * CryptoDevBackendAsymOpInfo: + * + * @src_len: byte length of source data + * @dst_len: byte length of destination data + * @src: point to the source data + * @dst: point to the destination data + * + */ +typedef struct CryptoDevBackendAsymOpInfo { + uint32_t src_len; + uint32_t dst_len; + uint8_t *src; + uint8_t *dst; +} CryptoDevBackendAsymOpInfo; + +typedef struct CryptoDevBackendOpInfo { + enum CryptoDevBackendAlgType algtype; + uint32_t op_code; + uint64_t session_id; + union { + CryptoDevBackendSymOpInfo *sym_op_info; + CryptoDevBackendAsymOpInfo *asym_op_info; + } u; +} CryptoDevBackendOpInfo; + struct CryptoDevBackendClass { ObjectClass parent_class; @@ -145,13 +195,13 @@ struct CryptoDevBackendClass { void (*cleanup)(CryptoDevBackend *backend, Error **errp); int64_t (*create_session)(CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, + CryptoDevBackendSessionInfo *sess_info, uint32_t queue_index, Error **errp); int (*close_session)(CryptoDevBackend *backend, uint64_t session_id, uint32_t queue_index, Error **errp); - int (*do_sym_op)(CryptoDevBackend *backend, - CryptoDevBackendSymOpInfo *op_info, + int (*do_op)(CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info, uint32_t queue_index, Error **errp); }; @@ -190,6 +240,7 @@ struct CryptoDevBackendConf { uint32_t mac_algo_l; uint32_t mac_algo_h; uint32_t aead_algo; + uint32_t akcipher_algo; /* Maximum length of cipher key */ uint32_t max_cipher_key_len; /* Maximum length of authenticated key */ @@ -247,34 +298,34 @@ void cryptodev_backend_cleanup( Error **errp); /** - * cryptodev_backend_sym_create_session: + * cryptodev_backend_create_session: * @backend: the cryptodev backend object * @sess_info: parameters needed by session creating * @queue_index: queue index of cryptodev backend client * @errp: pointer to a NULL-initialized error object * - * Create a session for symmetric algorithms + * Create a session for symmetric/symmetric algorithms * * Returns: session id on success, or -1 on error */ -int64_t cryptodev_backend_sym_create_session( +int64_t cryptodev_backend_create_session( CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, + CryptoDevBackendSessionInfo *sess_info, uint32_t queue_index, Error **errp); /** - * cryptodev_backend_sym_close_session: + * cryptodev_backend_close_session: * @backend: the cryptodev backend object * @session_id: the session id * @queue_index: queue index of cryptodev backend client * @errp: pointer to a NULL-initialized error object * - * Close a session for symmetric algorithms which was previously - * created by cryptodev_backend_sym_create_session() + * Close a session for which was previously + * created by cryptodev_backend_create_session() * * Returns: 0 on success, or Negative on error */ -int cryptodev_backend_sym_close_session( +int cryptodev_backend_close_session( CryptoDevBackend *backend, uint64_t session_id, uint32_t queue_index, Error **errp); diff --git a/include/sysemu/dirtylimit.h b/include/sysemu/dirtylimit.h new file mode 100644 index 00000000000..8d2c1f3a6b9 --- /dev/null +++ b/include/sysemu/dirtylimit.h @@ -0,0 +1,37 @@ +/* + * Dirty page rate limit common functions + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(黄勇) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_DIRTYRLIMIT_H +#define QEMU_DIRTYRLIMIT_H + +#define DIRTYLIMIT_CALC_TIME_MS 1000 /* 1000ms */ + +int64_t vcpu_dirty_rate_get(int cpu_index); +void vcpu_dirty_rate_stat_start(void); +void vcpu_dirty_rate_stat_stop(void); +void vcpu_dirty_rate_stat_initialize(void); +void vcpu_dirty_rate_stat_finalize(void); + +void dirtylimit_state_lock(void); +void dirtylimit_state_unlock(void); +void dirtylimit_state_initialize(void); +void dirtylimit_state_finalize(void); +bool dirtylimit_in_service(void); +bool dirtylimit_vcpu_index_valid(int cpu_index); +void dirtylimit_process(void); +void dirtylimit_change(bool start); +void dirtylimit_set_vcpu(int cpu_index, + uint64_t quota, + bool enable); +void dirtylimit_set_all(uint64_t quota, + bool enable); +void dirtylimit_vcpu_execute(CPUState *cpu); +#endif diff --git a/include/sysemu/dirtyrate.h b/include/sysemu/dirtyrate.h new file mode 100644 index 00000000000..4d3b9a49022 --- /dev/null +++ b/include/sysemu/dirtyrate.h @@ -0,0 +1,28 @@ +/* + * dirty page rate helper functions + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(黄勇) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_DIRTYRATE_H +#define QEMU_DIRTYRATE_H + +typedef struct VcpuStat { + int nvcpu; /* number of vcpu */ + DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ +} VcpuStat; + +int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, + VcpuStat *stat, + unsigned int flag, + bool one_shot); + +void global_dirty_log_change(unsigned int flag, + bool start); +#endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a783c788681..efd6dee818f 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -582,4 +582,6 @@ 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/ui/console.h b/include/ui/console.h index b64d8243609..c0520c694c2 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -463,6 +463,7 @@ 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); diff --git a/io/channel-null.c b/io/channel-null.c new file mode 100644 index 00000000000..75e37815070 --- /dev/null +++ b/io/channel-null.c @@ -0,0 +1,237 @@ +/* + * QEMU I/O channels null driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-null.h" +#include "io/channel-watch.h" +#include "qapi/error.h" +#include "trace.h" +#include "qemu/iov.h" + +typedef struct QIOChannelNullSource QIOChannelNullSource; +struct QIOChannelNullSource { + GSource parent; + QIOChannel *ioc; + GIOCondition condition; +}; + + +QIOChannelNull * +qio_channel_null_new(void) +{ + QIOChannelNull *ioc; + + ioc = QIO_CHANNEL_NULL(object_new(TYPE_QIO_CHANNEL_NULL)); + + trace_qio_channel_null_new(ioc); + + return ioc; +} + + +static void +qio_channel_null_init(Object *obj) +{ + QIOChannelNull *ioc = QIO_CHANNEL_NULL(obj); + ioc->closed = false; +} + + +static ssize_t +qio_channel_null_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds G_GNUC_UNUSED, + size_t *nfds G_GNUC_UNUSED, + Error **errp) +{ + QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); + + if (nioc->closed) { + error_setg_errno(errp, EINVAL, + "Channel is closed"); + return -1; + } + + return 0; +} + + +static ssize_t +qio_channel_null_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds G_GNUC_UNUSED, + size_t nfds G_GNUC_UNUSED, + int flags G_GNUC_UNUSED, + Error **errp) +{ + QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); + + if (nioc->closed) { + error_setg_errno(errp, EINVAL, + "Channel is closed"); + return -1; + } + + return iov_size(iov, niov); +} + + +static int +qio_channel_null_set_blocking(QIOChannel *ioc G_GNUC_UNUSED, + bool enabled G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return 0; +} + + +static off_t +qio_channel_null_seek(QIOChannel *ioc G_GNUC_UNUSED, + off_t offset G_GNUC_UNUSED, + int whence G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return 0; +} + + +static int +qio_channel_null_close(QIOChannel *ioc, + Error **errp G_GNUC_UNUSED) +{ + QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); + + nioc->closed = true; + return 0; +} + + +static void +qio_channel_null_set_aio_fd_handler(QIOChannel *ioc G_GNUC_UNUSED, + AioContext *ctx G_GNUC_UNUSED, + IOHandler *io_read G_GNUC_UNUSED, + IOHandler *io_write G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ +} + + +static gboolean +qio_channel_null_source_prepare(GSource *source G_GNUC_UNUSED, + gint *timeout) +{ + *timeout = -1; + + return TRUE; +} + + +static gboolean +qio_channel_null_source_check(GSource *source G_GNUC_UNUSED) +{ + return TRUE; +} + + +static gboolean +qio_channel_null_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelNullSource *ssource = (QIOChannelNullSource *)source; + + return (*func)(ssource->ioc, + ssource->condition, + user_data); +} + + +static void +qio_channel_null_source_finalize(GSource *source) +{ + QIOChannelNullSource *ssource = (QIOChannelNullSource *)source; + + object_unref(OBJECT(ssource->ioc)); +} + + +GSourceFuncs qio_channel_null_source_funcs = { + qio_channel_null_source_prepare, + qio_channel_null_source_check, + qio_channel_null_source_dispatch, + qio_channel_null_source_finalize +}; + + +static GSource * +qio_channel_null_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + GSource *source; + QIOChannelNullSource *ssource; + + source = g_source_new(&qio_channel_null_source_funcs, + sizeof(QIOChannelNullSource)); + ssource = (QIOChannelNullSource *)source; + + ssource->ioc = ioc; + object_ref(OBJECT(ioc)); + + ssource->condition = condition; + + return source; +} + + +static void +qio_channel_null_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_null_writev; + ioc_klass->io_readv = qio_channel_null_readv; + ioc_klass->io_set_blocking = qio_channel_null_set_blocking; + ioc_klass->io_seek = qio_channel_null_seek; + ioc_klass->io_close = qio_channel_null_close; + ioc_klass->io_create_watch = qio_channel_null_create_watch; + ioc_klass->io_set_aio_fd_handler = qio_channel_null_set_aio_fd_handler; +} + + +static const TypeInfo qio_channel_null_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_NULL, + .instance_size = sizeof(QIOChannelNull), + .instance_init = qio_channel_null_init, + .class_init = qio_channel_null_class_init, +}; + + +static void +qio_channel_null_register_types(void) +{ + type_register_static(&qio_channel_null_info); +} + +type_init(qio_channel_null_register_types); diff --git a/io/channel-socket.c b/io/channel-socket.c index dc9c165de11..74a936cc1f8 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -578,11 +578,17 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc, memcpy(CMSG_DATA(cmsg), fds, fdsize); } -#ifdef QEMU_MSG_ZEROCOPY if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { +#ifdef QEMU_MSG_ZEROCOPY sflags = MSG_ZEROCOPY; - } +#else + /* + * We expect QIOChannel class entry point to have + * blocked this code path already + */ + g_assert_not_reached(); #endif + } retry: ret = sendmsg(sioc->fd, &msg, sflags); @@ -592,21 +598,24 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc, return QIO_CHANNEL_ERR_BLOCK; case EINTR: goto retry; -#ifdef QEMU_MSG_ZEROCOPY case ENOBUFS: - if (sflags & MSG_ZEROCOPY) { + if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { error_setg_errno(errp, errno, "Process can't lock enough memory for using MSG_ZEROCOPY"); return -1; } break; -#endif } error_setg_errno(errp, errno, "Unable to write to socket"); return -1; } + + if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { + sioc->zero_copy_queued++; + } + return ret; } #else /* WIN32 */ @@ -707,12 +716,18 @@ static int qio_channel_socket_flush(QIOChannel *ioc, struct cmsghdr *cm; char control[CMSG_SPACE(sizeof(*serr))]; int received; - int ret = 1; + int ret; + + if (sioc->zero_copy_queued == sioc->zero_copy_sent) { + return 0; + } msg.msg_control = control; msg.msg_controllen = sizeof(control); memset(control, 0, sizeof(control)); + ret = 1; + while (sioc->zero_copy_sent < sioc->zero_copy_queued) { received = recvmsg(sioc->fd, &msg, MSG_ERRQUEUE); if (received < 0) { diff --git a/io/meson.build b/io/meson.build index bbcd3c53a4a..283b9b2bdbd 100644 --- a/io/meson.build +++ b/io/meson.build @@ -3,6 +3,7 @@ io_ss.add(files( 'channel-buffer.c', 'channel-command.c', 'channel-file.c', + 'channel-null.c', 'channel-socket.c', 'channel-tls.c', 'channel-util.c', diff --git a/io/trace-events b/io/trace-events index c5e814eb446..3cc5cf1efdf 100644 --- a/io/trace-events +++ b/io/trace-events @@ -10,6 +10,9 @@ qio_task_thread_result(void *task) "Task thread result task=%p" qio_task_thread_source_attach(void *task, void *source) "Task thread source attach task=%p source=%p" qio_task_thread_source_cancel(void *task, void *source) "Task thread source cancel task=%p source=%p" +# channel-null.c +qio_channel_null_new(void *ioc) "Null new ioc=%p" + # channel-socket.c qio_channel_socket_new(void *ioc) "Socket new ioc=%p" qio_channel_socket_new_fd(void *ioc, int fd) "Socket new ioc=%p fd=%d" diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 0d05d02ee4f..f089349149a 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -2031,6 +2031,7 @@ struct kvm_stats_header { #define KVM_STATS_UNIT_BYTES (0x1 << KVM_STATS_UNIT_SHIFT) #define KVM_STATS_UNIT_SECONDS (0x2 << KVM_STATS_UNIT_SHIFT) #define KVM_STATS_UNIT_CYCLES (0x3 << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_BOOLEAN (0x4 << KVM_STATS_UNIT_SHIFT) #define KVM_STATS_UNIT_MAX KVM_STATS_UNIT_CYCLES #define KVM_STATS_BASE_SHIFT 8 diff --git a/linux-headers/linux/vduse.h b/linux-headers/linux/vduse.h new file mode 100644 index 00000000000..d47b004ce6c --- /dev/null +++ b/linux-headers/linux/vduse.h @@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _VDUSE_H_ +#define _VDUSE_H_ + +#include + +#define VDUSE_BASE 0x81 + +/* The ioctls for control device (/dev/vduse/control) */ + +#define VDUSE_API_VERSION 0 + +/* + * Get the version of VDUSE API that kernel supported (VDUSE_API_VERSION). + * This is used for future extension. + */ +#define VDUSE_GET_API_VERSION _IOR(VDUSE_BASE, 0x00, __u64) + +/* Set the version of VDUSE API that userspace supported. */ +#define VDUSE_SET_API_VERSION _IOW(VDUSE_BASE, 0x01, __u64) + +/** + * struct vduse_dev_config - basic configuration of a VDUSE device + * @name: VDUSE device name, needs to be NUL terminated + * @vendor_id: virtio vendor id + * @device_id: virtio device id + * @features: virtio features + * @vq_num: the number of virtqueues + * @vq_align: the allocation alignment of virtqueue's metadata + * @reserved: for future use, needs to be initialized to zero + * @config_size: the size of the configuration space + * @config: the buffer of the configuration space + * + * Structure used by VDUSE_CREATE_DEV ioctl to create VDUSE device. + */ +struct vduse_dev_config { +#define VDUSE_NAME_MAX 256 + char name[VDUSE_NAME_MAX]; + __u32 vendor_id; + __u32 device_id; + __u64 features; + __u32 vq_num; + __u32 vq_align; + __u32 reserved[13]; + __u32 config_size; + __u8 config[]; +}; + +/* Create a VDUSE device which is represented by a char device (/dev/vduse/$NAME) */ +#define VDUSE_CREATE_DEV _IOW(VDUSE_BASE, 0x02, struct vduse_dev_config) + +/* + * Destroy a VDUSE device. Make sure there are no more references + * to the char device (/dev/vduse/$NAME). + */ +#define VDUSE_DESTROY_DEV _IOW(VDUSE_BASE, 0x03, char[VDUSE_NAME_MAX]) + +/* The ioctls for VDUSE device (/dev/vduse/$NAME) */ + +/** + * struct vduse_iotlb_entry - entry of IOTLB to describe one IOVA region [start, last] + * @offset: the mmap offset on returned file descriptor + * @start: start of the IOVA region + * @last: last of the IOVA region + * @perm: access permission of the IOVA region + * + * Structure used by VDUSE_IOTLB_GET_FD ioctl to find an overlapped IOVA region. + */ +struct vduse_iotlb_entry { + __u64 offset; + __u64 start; + __u64 last; +#define VDUSE_ACCESS_RO 0x1 +#define VDUSE_ACCESS_WO 0x2 +#define VDUSE_ACCESS_RW 0x3 + __u8 perm; +}; + +/* + * Find the first IOVA region that overlaps with the range [start, last] + * and return the corresponding file descriptor. Return -EINVAL means the + * IOVA region doesn't exist. Caller should set start and last fields. + */ +#define VDUSE_IOTLB_GET_FD _IOWR(VDUSE_BASE, 0x10, struct vduse_iotlb_entry) + +/* + * Get the negotiated virtio features. It's a subset of the features in + * struct vduse_dev_config which can be accepted by virtio driver. It's + * only valid after FEATURES_OK status bit is set. + */ +#define VDUSE_DEV_GET_FEATURES _IOR(VDUSE_BASE, 0x11, __u64) + +/** + * struct vduse_config_data - data used to update configuration space + * @offset: the offset from the beginning of configuration space + * @length: the length to write to configuration space + * @buffer: the buffer used to write from + * + * Structure used by VDUSE_DEV_SET_CONFIG ioctl to update device + * configuration space. + */ +struct vduse_config_data { + __u32 offset; + __u32 length; + __u8 buffer[]; +}; + +/* Set device configuration space */ +#define VDUSE_DEV_SET_CONFIG _IOW(VDUSE_BASE, 0x12, struct vduse_config_data) + +/* + * Inject a config interrupt. It's usually used to notify virtio driver + * that device configuration space has changed. + */ +#define VDUSE_DEV_INJECT_CONFIG_IRQ _IO(VDUSE_BASE, 0x13) + +/** + * struct vduse_vq_config - basic configuration of a virtqueue + * @index: virtqueue index + * @max_size: the max size of virtqueue + * @reserved: for future use, needs to be initialized to zero + * + * Structure used by VDUSE_VQ_SETUP ioctl to setup a virtqueue. + */ +struct vduse_vq_config { + __u32 index; + __u16 max_size; + __u16 reserved[13]; +}; + +/* + * Setup the specified virtqueue. Make sure all virtqueues have been + * configured before the device is attached to vDPA bus. + */ +#define VDUSE_VQ_SETUP _IOW(VDUSE_BASE, 0x14, struct vduse_vq_config) + +/** + * struct vduse_vq_state_split - split virtqueue state + * @avail_index: available index + */ +struct vduse_vq_state_split { + __u16 avail_index; +}; + +/** + * struct vduse_vq_state_packed - packed virtqueue state + * @last_avail_counter: last driver ring wrap counter observed by device + * @last_avail_idx: device available index + * @last_used_counter: device ring wrap counter + * @last_used_idx: used index + */ +struct vduse_vq_state_packed { + __u16 last_avail_counter; + __u16 last_avail_idx; + __u16 last_used_counter; + __u16 last_used_idx; +}; + +/** + * struct vduse_vq_info - information of a virtqueue + * @index: virtqueue index + * @num: the size of virtqueue + * @desc_addr: address of desc area + * @driver_addr: address of driver area + * @device_addr: address of device area + * @split: split virtqueue state + * @packed: packed virtqueue state + * @ready: ready status of virtqueue + * + * Structure used by VDUSE_VQ_GET_INFO ioctl to get virtqueue's information. + */ +struct vduse_vq_info { + __u32 index; + __u32 num; + __u64 desc_addr; + __u64 driver_addr; + __u64 device_addr; + union { + struct vduse_vq_state_split split; + struct vduse_vq_state_packed packed; + }; + __u8 ready; +}; + +/* Get the specified virtqueue's information. Caller should set index field. */ +#define VDUSE_VQ_GET_INFO _IOWR(VDUSE_BASE, 0x15, struct vduse_vq_info) + +/** + * struct vduse_vq_eventfd - eventfd configuration for a virtqueue + * @index: virtqueue index + * @fd: eventfd, -1 means de-assigning the eventfd + * + * Structure used by VDUSE_VQ_SETUP_KICKFD ioctl to setup kick eventfd. + */ +struct vduse_vq_eventfd { + __u32 index; +#define VDUSE_EVENTFD_DEASSIGN -1 + int fd; +}; + +/* + * Setup kick eventfd for specified virtqueue. The kick eventfd is used + * by VDUSE kernel module to notify userspace to consume the avail vring. + */ +#define VDUSE_VQ_SETUP_KICKFD _IOW(VDUSE_BASE, 0x16, struct vduse_vq_eventfd) + +/* + * Inject an interrupt for specific virtqueue. It's used to notify virtio driver + * to consume the used vring. + */ +#define VDUSE_VQ_INJECT_IRQ _IOW(VDUSE_BASE, 0x17, __u32) + +/* The control messages definition for read(2)/write(2) on /dev/vduse/$NAME */ + +/** + * enum vduse_req_type - request type + * @VDUSE_GET_VQ_STATE: get the state for specified virtqueue from userspace + * @VDUSE_SET_STATUS: set the device status + * @VDUSE_UPDATE_IOTLB: Notify userspace to update the memory mapping for + * specified IOVA range via VDUSE_IOTLB_GET_FD ioctl + */ +enum vduse_req_type { + VDUSE_GET_VQ_STATE, + VDUSE_SET_STATUS, + VDUSE_UPDATE_IOTLB, +}; + +/** + * struct vduse_vq_state - virtqueue state + * @index: virtqueue index + * @split: split virtqueue state + * @packed: packed virtqueue state + */ +struct vduse_vq_state { + __u32 index; + union { + struct vduse_vq_state_split split; + struct vduse_vq_state_packed packed; + }; +}; + +/** + * struct vduse_dev_status - device status + * @status: device status + */ +struct vduse_dev_status { + __u8 status; +}; + +/** + * struct vduse_iova_range - IOVA range [start, last] + * @start: start of the IOVA range + * @last: last of the IOVA range + */ +struct vduse_iova_range { + __u64 start; + __u64 last; +}; + +/** + * struct vduse_dev_request - control request + * @type: request type + * @request_id: request id + * @reserved: for future use + * @vq_state: virtqueue state, only index field is available + * @s: device status + * @iova: IOVA range for updating + * @padding: padding + * + * Structure used by read(2) on /dev/vduse/$NAME. + */ +struct vduse_dev_request { + __u32 type; + __u32 request_id; + __u32 reserved[4]; + union { + struct vduse_vq_state vq_state; + struct vduse_dev_status s; + struct vduse_iova_range iova; + __u32 padding[32]; + }; +}; + +/** + * struct vduse_dev_response - response to control request + * @request_id: corresponding request id + * @result: the result of request + * @reserved: for future use, needs to be initialized to zero + * @vq_state: virtqueue state + * @padding: padding + * + * Structure used by write(2) on /dev/vduse/$NAME. + */ +struct vduse_dev_response { + __u32 request_id; +#define VDUSE_REQ_RESULT_OK 0x00 +#define VDUSE_REQ_RESULT_FAILED 0x01 + __u32 result; + __u32 reserved[4]; + union { + struct vduse_vq_state vq_state; + __u32 padding[32]; + }; +}; + +#endif /* _VDUSE_H_ */ diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index f3f19fb0901..6b45ed0041f 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -99,6 +99,15 @@ void cpu_loop(CPUARMState *env) //// --- End LibAFL code --- case EXCP_SWI: + /* + * On syscall, PSTATE.ZA is preserved, along with the ZA matrix. + * PSTATE.SM is cleared, per SMSTOP, which does ResetSVEState. + */ + if (FIELD_EX64(env->svcr, SVCR, SM)) { + env->svcr = FIELD_DP64(env->svcr, SVCR, SM, 0); + arm_rebuild_hflags(env); + arm_reset_sve_state(env); + } ret = do_syscall(env, env->xregs[8], env->xregs[0], @@ -164,7 +173,7 @@ void cpu_loop(CPUARMState *env) force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case EXCP_SEMIHOST: - env->xregs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; break; case EXCP_YIELD: diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 7da0e36c6d4..6a2c6e06d28 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -78,7 +78,8 @@ struct target_extra_context { struct target_sve_context { struct target_aarch64_ctx head; uint16_t vl; - uint16_t reserved[3]; + uint16_t flags; + uint16_t reserved[2]; /* The actual SVE data immediately follows. It is laid out * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of * the original struct pointer. @@ -101,6 +102,24 @@ struct target_sve_context { #define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) +#define TARGET_SVE_SIG_FLAG_SM 1 + +#define TARGET_ZA_MAGIC 0x54366345 + +struct target_za_context { + struct target_aarch64_ctx head; + uint16_t vl; + uint16_t reserved[3]; + /* The actual ZA data immediately follows. */ +}; + +#define TARGET_ZA_SIG_REGS_OFFSET \ + QEMU_ALIGN_UP(sizeof(struct target_za_context), TARGET_SVE_VQ_BYTES) +#define TARGET_ZA_SIG_ZAV_OFFSET(VQ, N) \ + (TARGET_ZA_SIG_REGS_OFFSET + (VQ) * TARGET_SVE_VQ_BYTES * (N)) +#define TARGET_ZA_SIG_CONTEXT_SIZE(VQ) \ + TARGET_ZA_SIG_ZAV_OFFSET(VQ, VQ * TARGET_SVE_VQ_BYTES) + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -173,13 +192,17 @@ static void target_setup_end_record(struct target_aarch64_ctx *end) } static void target_setup_sve_record(struct target_sve_context *sve, - CPUARMState *env, int vq, int size) + CPUARMState *env, int size) { - int i, j; + int i, j, vq = sve_vq(env); + memset(sve, 0, sizeof(*sve)); __put_user(TARGET_SVE_MAGIC, &sve->head.magic); __put_user(size, &sve->head.size); __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); + if (FIELD_EX64(env->svcr, SVCR, SM)) { + __put_user(TARGET_SVE_SIG_FLAG_SM, &sve->flags); + } /* Note that SVE regs are stored as a byte stream, with each byte element * at a subsequent address. This corresponds to a little-endian store @@ -200,6 +223,35 @@ static void target_setup_sve_record(struct target_sve_context *sve, } } +static void target_setup_za_record(struct target_za_context *za, + CPUARMState *env, int size) +{ + int vq = sme_vq(env); + int vl = vq * TARGET_SVE_VQ_BYTES; + int i, j; + + memset(za, 0, sizeof(*za)); + __put_user(TARGET_ZA_MAGIC, &za->head.magic); + __put_user(size, &za->head.size); + __put_user(vl, &za->vl); + + if (size == TARGET_ZA_SIG_CONTEXT_SIZE(0)) { + return; + } + assert(size == TARGET_ZA_SIG_CONTEXT_SIZE(vq)); + + /* + * Note that ZA vectors are stored as a byte stream, + * with each byte element at a subsequent address. + */ + for (i = 0; i < vl; ++i) { + uint64_t *z = (void *)za + TARGET_ZA_SIG_ZAV_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __put_user_e(env->zarray[i].d[j], z + j, le); + } + } +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -243,12 +295,50 @@ static void target_restore_fpsimd_record(CPUARMState *env, } } -static void target_restore_sve_record(CPUARMState *env, - struct target_sve_context *sve, int vq) +static bool target_restore_sve_record(CPUARMState *env, + struct target_sve_context *sve, + int size, int *svcr) { - int i, j; + int i, j, vl, vq, flags; + bool sm; - /* Note that SVE regs are stored as a byte stream, with each byte element + __get_user(vl, &sve->vl); + __get_user(flags, &sve->flags); + + sm = flags & TARGET_SVE_SIG_FLAG_SM; + + /* The cpu must support Streaming or Non-streaming SVE. */ + if (sm + ? !cpu_isar_feature(aa64_sme, env_archcpu(env)) + : !cpu_isar_feature(aa64_sve, env_archcpu(env))) { + return false; + } + + /* + * Note that we cannot use sve_vq() because that depends on the + * current setting of PSTATE.SM, not the state to be restored. + */ + vq = sve_vqm1_for_el_sm(env, 0, sm) + 1; + + /* Reject mismatched VL. */ + if (vl != vq * TARGET_SVE_VQ_BYTES) { + return false; + } + + /* Accept empty record -- used to clear PSTATE.SM. */ + if (size <= sizeof(*sve)) { + return true; + } + + /* Reject non-empty but incomplete record. */ + if (size < TARGET_SVE_SIG_CONTEXT_SIZE(vq)) { + return false; + } + + *svcr = FIELD_DP64(*svcr, SVCR, SM, sm); + + /* + * Note that SVE regs are stored as a byte stream, with each byte element * at a subsequent address. This corresponds to a little-endian load * of our 64-bit hunks. */ @@ -270,6 +360,46 @@ static void target_restore_sve_record(CPUARMState *env, } } } + return true; +} + +static bool target_restore_za_record(CPUARMState *env, + struct target_za_context *za, + int size, int *svcr) +{ + int i, j, vl, vq; + + if (!cpu_isar_feature(aa64_sme, env_archcpu(env))) { + return false; + } + + __get_user(vl, &za->vl); + vq = sme_vq(env); + + /* Reject mismatched VL. */ + if (vl != vq * TARGET_SVE_VQ_BYTES) { + return false; + } + + /* Accept empty record -- used to clear PSTATE.ZA. */ + if (size <= TARGET_ZA_SIG_CONTEXT_SIZE(0)) { + return true; + } + + /* Reject non-empty but incomplete record. */ + if (size < TARGET_ZA_SIG_CONTEXT_SIZE(vq)) { + return false; + } + + *svcr = FIELD_DP64(*svcr, SVCR, ZA, 1); + + for (i = 0; i < vl; ++i) { + uint64_t *z = (void *)za + TARGET_ZA_SIG_ZAV_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __get_user_e(env->zarray[i].d[j], z + j, le); + } + } + return true; } static int target_restore_sigframe(CPUARMState *env, @@ -278,10 +408,12 @@ static int target_restore_sigframe(CPUARMState *env, struct target_aarch64_ctx *ctx, *extra = NULL; struct target_fpsimd_context *fpsimd = NULL; struct target_sve_context *sve = NULL; + struct target_za_context *za = NULL; uint64_t extra_datap = 0; bool used_extra = false; - bool err = false; - int vq = 0, sve_size = 0; + int sve_size = 0; + int za_size = 0; + int svcr = 0; target_restore_general_frame(env, sf); @@ -294,8 +426,7 @@ static int target_restore_sigframe(CPUARMState *env, switch (magic) { case 0: if (size != 0) { - err = true; - goto exit; + goto err; } if (used_extra) { ctx = NULL; @@ -307,42 +438,46 @@ static int target_restore_sigframe(CPUARMState *env, case TARGET_FPSIMD_MAGIC: if (fpsimd || size != sizeof(struct target_fpsimd_context)) { - err = true; - goto exit; + goto err; } fpsimd = (struct target_fpsimd_context *)ctx; break; case TARGET_SVE_MAGIC: - if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { - vq = sve_vq(env); - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); - if (!sve && size == sve_size) { - sve = (struct target_sve_context *)ctx; - break; - } + if (sve || size < sizeof(struct target_sve_context)) { + goto err; } - err = true; - goto exit; + sve = (struct target_sve_context *)ctx; + sve_size = size; + break; + + case TARGET_ZA_MAGIC: + if (za || size < sizeof(struct target_za_context)) { + goto err; + } + za = (struct target_za_context *)ctx; + za_size = size; + break; case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { - err = true; - goto exit; + goto err; } __get_user(extra_datap, &((struct target_extra_context *)ctx)->datap); __get_user(extra_size, &((struct target_extra_context *)ctx)->size); extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); + if (!extra) { + return 1; + } break; default: /* Unknown record -- we certainly didn't generate it. * Did we in fact get out of sync? */ - err = true; - goto exit; + goto err; } ctx = (void *)ctx + size; } @@ -351,17 +486,26 @@ static int target_restore_sigframe(CPUARMState *env, if (fpsimd) { target_restore_fpsimd_record(env, fpsimd); } else { - err = true; + goto err; } /* SVE data, if present, overwrites FPSIMD data. */ - if (sve) { - target_restore_sve_record(env, sve, vq); + if (sve && !target_restore_sve_record(env, sve, sve_size, &svcr)) { + goto err; + } + if (za && !target_restore_za_record(env, za, za_size, &svcr)) { + goto err; + } + if (env->svcr != svcr) { + env->svcr = svcr; + arm_rebuild_hflags(env); } + unlock_user(extra, extra_datap, 0); + return 0; - exit: + err: unlock_user(extra, extra_datap, 0); - return err; + return 1; } static abi_ulong get_sigframe(struct target_sigaction *ka, @@ -423,7 +567,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, .total_size = offsetof(struct target_rt_sigframe, uc.tuc_mcontext.__reserved), }; - int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0; + int sve_size = 0, za_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; @@ -433,11 +578,20 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, &layout); /* SVE state needs saving only if it exists. */ - if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { - vq = sve_vq(env); - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + if (cpu_isar_feature(aa64_sve, env_archcpu(env)) || + cpu_isar_feature(aa64_sme, env_archcpu(env))) { + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(sve_vq(env)), 16); sve_ofs = alloc_sigframe_space(sve_size, &layout); } + if (cpu_isar_feature(aa64_sme, env_archcpu(env))) { + /* ZA state needs saving only if it is enabled. */ + if (FIELD_EX64(env->svcr, SVCR, ZA)) { + za_size = TARGET_ZA_SIG_CONTEXT_SIZE(sme_vq(env)); + } else { + za_size = TARGET_ZA_SIG_CONTEXT_SIZE(0); + } + za_ofs = alloc_sigframe_space(za_size, &layout); + } if (layout.extra_ofs) { /* Reserve space for the extra end marker. The standard end marker @@ -484,7 +638,10 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, target_setup_end_record((void *)frame + layout.extra_end_ofs); } if (sve_ofs) { - target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); + target_setup_sve_record((void *)frame + sve_ofs, env, sve_size); + } + if (za_ofs) { + target_setup_za_record((void *)frame + za_ofs, env, za_size); } /* Set up the stack frame for unwinding. */ @@ -508,6 +665,18 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, env->btype = 2; } + /* + * Invoke the signal handler with both SM and ZA disabled. + * When clearing SM, ResetSVEState, per SMSTOP. + */ + if (FIELD_EX64(env->svcr, SVCR, SM)) { + arm_reset_sve_state(env); + } + if (env->svcr) { + env->svcr = 0; + arm_rebuild_hflags(env); + } + if (info) { tswap_siginfo(&frame->info, info); env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h index 97a477bd3e9..f90359faf20 100644 --- a/linux-user/aarch64/target_cpu.h +++ b/linux-user/aarch64/target_cpu.h @@ -34,10 +34,13 @@ static inline void cpu_clone_regs_parent(CPUARMState *env, unsigned flags) static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) { - /* Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is + /* + * Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is * different from AArch32 Linux, which uses TPIDRRO. */ env->cp15.tpidr_el[0] = newtls; + /* TPIDR2_EL0 is cleared with CLONE_SETTLS. */ + env->cp15.tpidr2_el0 = 0; } static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) diff --git a/linux-user/aarch64/target_prctl.h b/linux-user/aarch64/target_prctl.h index 1d440ffbea4..907c3141466 100644 --- a/linux-user/aarch64/target_prctl.h +++ b/linux-user/aarch64/target_prctl.h @@ -6,17 +6,18 @@ #ifndef AARCH64_TARGET_PRCTL_H #define AARCH64_TARGET_PRCTL_H -static abi_long do_prctl_get_vl(CPUArchState *env) +static abi_long do_prctl_sve_get_vl(CPUArchState *env) { ARMCPU *cpu = env_archcpu(env); if (cpu_isar_feature(aa64_sve, cpu)) { + /* PSTATE.SM is always unset on syscall entry. */ return sve_vq(env) * 16; } return -TARGET_EINVAL; } -#define do_prctl_get_vl do_prctl_get_vl +#define do_prctl_sve_get_vl do_prctl_sve_get_vl -static abi_long do_prctl_set_vl(CPUArchState *env, abi_long arg2) +static abi_long do_prctl_sve_set_vl(CPUArchState *env, abi_long arg2) { /* * We cannot support either PR_SVE_SET_VL_ONEXEC or PR_SVE_VL_INHERIT. @@ -27,6 +28,7 @@ static abi_long do_prctl_set_vl(CPUArchState *env, abi_long arg2) && arg2 >= 0 && arg2 <= 512 * 16 && !(arg2 & 15)) { uint32_t vq, old_vq; + /* PSTATE.SM is always unset on syscall entry. */ old_vq = sve_vq(env); /* @@ -47,7 +49,59 @@ static abi_long do_prctl_set_vl(CPUArchState *env, abi_long arg2) } return -TARGET_EINVAL; } -#define do_prctl_set_vl do_prctl_set_vl +#define do_prctl_sve_set_vl do_prctl_sve_set_vl + +static abi_long do_prctl_sme_get_vl(CPUArchState *env) +{ + ARMCPU *cpu = env_archcpu(env); + if (cpu_isar_feature(aa64_sme, cpu)) { + return sme_vq(env) * 16; + } + return -TARGET_EINVAL; +} +#define do_prctl_sme_get_vl do_prctl_sme_get_vl + +static abi_long do_prctl_sme_set_vl(CPUArchState *env, abi_long arg2) +{ + /* + * We cannot support either PR_SME_SET_VL_ONEXEC or PR_SME_VL_INHERIT. + * Note the kernel definition of sve_vl_valid allows for VQ=512, + * i.e. VL=8192, even though the architectural maximum is VQ=16. + */ + if (cpu_isar_feature(aa64_sme, env_archcpu(env)) + && arg2 >= 0 && arg2 <= 512 * 16 && !(arg2 & 15)) { + int vq, old_vq; + + old_vq = sme_vq(env); + + /* + * Bound the value of vq, so that we know that it fits into + * the 4-bit field in SMCR_EL1. Because PSTATE.SM is cleared + * on syscall entry, we are not modifying the current SVE + * vector length. + */ + vq = MAX(arg2 / 16, 1); + vq = MIN(vq, 16); + env->vfp.smcr_el[1] = + FIELD_DP64(env->vfp.smcr_el[1], SMCR, LEN, vq - 1); + + /* Delay rebuilding hflags until we know if ZA must change. */ + vq = sve_vqm1_for_el_sm(env, 0, true) + 1; + + if (vq != old_vq) { + /* + * PSTATE.ZA state is cleared on any change to SVL. + * We need not call arm_rebuild_hflags because PSTATE.SM was + * cleared on syscall entry, so this hasn't changed VL. + */ + env->svcr = FIELD_DP64(env->svcr, SVCR, ZA, 0); + arm_rebuild_hflags(env); + } + return vq * 16; + } + return -TARGET_EINVAL; +} +#define do_prctl_sme_set_vl do_prctl_sme_set_vl static abi_long do_prctl_reset_keys(CPUArchState *env, abi_long arg2) { diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index a1e9005b6ed..628afdb1582 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -459,7 +459,7 @@ void cpu_loop(CPUARMState *env) } break; case EXCP_SEMIHOST: - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->regs[15] += env->thumb ? 2 : 4; break; case EXCP_INTERRUPT: diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f7eae357f4a..ce902dbd56b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,19 +130,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_PLATFORM get_elf_platform() - -static const char *get_elf_platform(void) -{ - static char elf_platform[] = "i386"; - int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); - if (family > 6) - family = 6; - if (family >= 3) - elf_platform[1] = '0' + family; - return elf_platform; -} - #define ELF_HWCAP get_elf_hwcap() static uint32_t get_elf_hwcap(void) @@ -158,6 +145,8 @@ static uint32_t get_elf_hwcap(void) #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 +#define ELF_PLATFORM "x86_64" + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->rax = 0; @@ -221,6 +210,21 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_386 +#define ELF_PLATFORM get_elf_platform() + +static const char *get_elf_platform(void) +{ + static char elf_platform[] = "i386"; + int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); + if (family > 6) { + family = 6; + } + if (family >= 3) { + elf_platform[1] = '0' + family; + } + return elf_platform; +} + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -601,6 +605,18 @@ enum { ARM_HWCAP2_A64_RNG = 1 << 16, ARM_HWCAP2_A64_BTI = 1 << 17, ARM_HWCAP2_A64_MTE = 1 << 18, + ARM_HWCAP2_A64_ECV = 1 << 19, + ARM_HWCAP2_A64_AFP = 1 << 20, + ARM_HWCAP2_A64_RPRES = 1 << 21, + ARM_HWCAP2_A64_MTE3 = 1 << 22, + ARM_HWCAP2_A64_SME = 1 << 23, + ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, + ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, + ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, + ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, + ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, + ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, + ARM_HWCAP2_A64_SME_FA64 = 1 << 30, }; #define ELF_HWCAP get_elf_hwcap() @@ -670,6 +686,14 @@ static uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | + ARM_HWCAP2_A64_SME_F32F32 | + ARM_HWCAP2_A64_SME_B16F32 | + ARM_HWCAP2_A64_SME_F16F32 | + ARM_HWCAP2_A64_SME_I8I32)); + GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); + GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); + GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); return hwcaps; } @@ -918,6 +942,97 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #endif +#ifdef TARGET_LOONGARCH64 + +#define ELF_START_MMAP 0x80000000 + +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_LOONGARCH + +#define elf_check_arch(x) ((x) == EM_LOONGARCH) + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + /*Set crmd PG,DA = 1,0 */ + regs->csr.crmd = 2 << 3; + regs->csr.era = infop->entry; + regs->regs[3] = infop->start_stack; +} + +/* See linux kernel: arch/loongarch/include/asm/elf.h */ +#define ELF_NREG 45 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +enum { + TARGET_EF_R0 = 0, + TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, + TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, +}; + +static void elf_core_copy_regs(target_elf_gregset_t *regs, + const CPULoongArchState *env) +{ + int i; + + (*regs)[TARGET_EF_R0] = 0; + + for (i = 1; i < ARRAY_SIZE(env->gpr); i++) { + (*regs)[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + } + + (*regs)[TARGET_EF_CSR_ERA] = tswapreg(env->pc); + (*regs)[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#define ELF_HWCAP get_elf_hwcap() + +/* See arch/loongarch/include/uapi/asm/hwcap.h */ +enum { + HWCAP_LOONGARCH_CPUCFG = (1 << 0), + HWCAP_LOONGARCH_LAM = (1 << 1), + HWCAP_LOONGARCH_UAL = (1 << 2), + HWCAP_LOONGARCH_FPU = (1 << 3), + HWCAP_LOONGARCH_LSX = (1 << 4), + HWCAP_LOONGARCH_LASX = (1 << 5), + HWCAP_LOONGARCH_CRC32 = (1 << 6), + HWCAP_LOONGARCH_COMPLEX = (1 << 7), + HWCAP_LOONGARCH_CRYPTO = (1 << 8), + HWCAP_LOONGARCH_LVZ = (1 << 9), + HWCAP_LOONGARCH_LBT_X86 = (1 << 10), + HWCAP_LOONGARCH_LBT_ARM = (1 << 11), + HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), +}; + +static uint32_t get_elf_hwcap(void) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(thread_cpu); + uint32_t hwcaps = 0; + + hwcaps |= HWCAP_LOONGARCH_CRC32; + + if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { + hwcaps |= HWCAP_LOONGARCH_UAL; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { + hwcaps |= HWCAP_LOONGARCH_FPU; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { + hwcaps |= HWCAP_LOONGARCH_LAM; + } + + return hwcaps; +} + +#define ELF_PLATFORM "loongarch" + +#endif /* TARGET_LOONGARCH64 */ + #ifdef TARGET_MIPS #define ELF_START_MMAP 0x80000000 diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c new file mode 100644 index 00000000000..894fdd111a8 --- /dev/null +++ b/linux-user/loongarch64/cpu_loop.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch user cpu_loop. + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "user-internals.h" +#include "cpu_loop-common.h" +#include "signal-common.h" + +void cpu_loop(CPULoongArchState *env) +{ + CPUState *cs = env_cpu(env); + int trapnr, si_code; + abi_long ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCCODE_SYS: + env->pc += 4; + ret = do_syscall(env, env->gpr[11], + env->gpr[4], env->gpr[5], + env->gpr[6], env->gpr[7], + env->gpr[8], env->gpr[9], + -1, -1); + if (ret == -QEMU_ERESTARTSYS) { + env->pc -= 4; + break; + } + if (ret == -QEMU_ESIGRETURN) { + /* + * Returning from a successful sigreturn syscall. + * Avoid clobbering register state. + */ + break; + } + env->gpr[4] = ret; + break; + case EXCCODE_INE: + force_sig_fault(TARGET_SIGILL, 0, env->pc); + break; + case EXCCODE_FPE: + si_code = TARGET_FPE_FLTUNK; + if (GET_FP_CAUSE(env->fcsr0) & FP_INVALID) { + si_code = TARGET_FPE_FLTINV; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_DIV0) { + si_code = TARGET_FPE_FLTDIV; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_OVERFLOW) { + si_code = TARGET_FPE_FLTOVF; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_UNDERFLOW) { + si_code = TARGET_FPE_FLTUND; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_INEXACT) { + si_code = TARGET_FPE_FLTRES; + } + force_sig_fault(TARGET_SIGFPE, si_code, env->pc); + break; + case EXCP_DEBUG: + case EXCCODE_BRK: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + break; + case EXCCODE_BCE: + force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); + exit(EXIT_FAILURE); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + + for (i = 0; i < 32; i++) { + env->gpr[i] = regs->regs[i]; + } + env->pc = regs->csr.era; + +} diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c new file mode 100644 index 00000000000..65fd5f3857b --- /dev/null +++ b/linux-user/loongarch64/signal.c @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch emulation of Linux signals + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "user-internals.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#include "target/loongarch/internals.h" + +/* 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); +}; + + +#define FPU_CTX_MAGIC 0x46505501 +#define FPU_CTX_ALIGN 8 +struct target_fpu_context { + uint64_t regs[32]; + uint64_t fcc; + uint32_t fcsr; +} QEMU_ALIGNED(FPU_CTX_ALIGN); + +#define CONTEXT_INFO_ALIGN 16 +struct target_sctx_info { + uint32_t magic; + uint32_t size; + uint64_t padding; +} QEMU_ALIGNED(CONTEXT_INFO_ALIGN); + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ptr tuc_link; + target_stack_t tuc_stack; + target_sigset_t tuc_sigmask; + uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)]; + struct target_sigcontext tuc_mcontext; +}; + +struct target_rt_sigframe { + struct target_siginfo rs_info; + struct target_ucontext rs_uc; +}; + +/* + * These two structures are not present in guest memory, are private + * to the signal implementation, but are largely copied from the + * kernel's signal implementation. + */ +struct ctx_layout { + void *haddr; + abi_ptr gaddr; + unsigned int size; +}; + +struct extctx_layout { + unsigned int size; + unsigned int flags; + struct ctx_layout fpu; + struct ctx_layout end; +}; + +/* The kernel's sc_save_fcc macro is a sequence of MOVCF2GR+BSTRINS. */ +static uint64_t read_all_fcc(CPULoongArchState *env) +{ + uint64_t ret = 0; + + for (int i = 0; i < 8; ++i) { + ret |= (uint64_t)env->cf[i] << (i * 8); + } + + return ret; +} + +/* The kernel's sc_restore_fcc macro is a sequence of BSTRPICK+MOVGR2CF. */ +static void write_all_fcc(CPULoongArchState *env, uint64_t val) +{ + for (int i = 0; i < 8; ++i) { + env->cf[i] = (val >> (i * 8)) & 1; + } +} + +static abi_ptr extframe_alloc(struct extctx_layout *extctx, + struct ctx_layout *sctx, unsigned size, + unsigned align, abi_ptr orig_sp) +{ + abi_ptr sp = orig_sp; + + sp -= sizeof(struct target_sctx_info) + size; + align = MAX(align, CONTEXT_INFO_ALIGN); + sp = ROUND_DOWN(sp, align); + sctx->gaddr = sp; + + size = orig_sp - sp; + sctx->size = size; + extctx->size += size; + + return sp; +} + +static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) +{ + memset(extctx, 0, sizeof(struct extctx_layout)); + + /* Grow down, alloc "end" context info first. */ + sp = extframe_alloc(extctx, &extctx->end, 0, CONTEXT_INFO_ALIGN, sp); + + /* For qemu, there is no lazy fp context switch, so fp always present. */ + extctx->flags = SC_USED_FP; + sp = extframe_alloc(extctx, &extctx->fpu, + sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp); + + return sp; +} + +static void setup_sigframe(CPULoongArchState *env, + struct target_sigcontext *sc, + struct extctx_layout *extctx) +{ + struct target_sctx_info *info; + struct target_fpu_context *fpu_ctx; + int i; + + __put_user(extctx->flags, &sc->sc_flags); + __put_user(env->pc, &sc->sc_pc); + __put_user(0, &sc->sc_regs[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->gpr[i], &sc->sc_regs[i]); + } + + /* + * Set fpu context + */ + info = extctx->fpu.haddr; + __put_user(FPU_CTX_MAGIC, &info->magic); + __put_user(extctx->fpu.size, &info->size); + + fpu_ctx = (struct target_fpu_context *)(info + 1); + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i], &fpu_ctx->regs[i]); + } + __put_user(read_all_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); +} + +static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) +{ + memset(extctx, 0, sizeof(*extctx)); + + while (1) { + uint32_t magic, size; + + if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) { + return false; + } + + switch (magic) { + case 0: /* END */ + extctx->end.gaddr = frame; + extctx->end.size = size; + extctx->size += size; + return true; + + case FPU_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_fpu_context))) { + return false; + } + extctx->fpu.gaddr = frame; + extctx->fpu.size = size; + extctx->size += size; + break; + default: + return false; + } + + frame += size; + } +} + +static void restore_sigframe(CPULoongArchState *env, + struct target_sigcontext *sc, + struct extctx_layout *extctx) +{ + int i; + + __get_user(env->pc, &sc->sc_pc); + for (i = 1; i < 32; ++i) { + __get_user(env->gpr[i], &sc->sc_regs[i]); + } + + if (extctx->fpu.haddr) { + struct target_fpu_context *fpu_ctx = + extctx->fpu.haddr + sizeof(struct target_sctx_info); + uint64_t fcc; + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i], &fpu_ctx->regs[i]); + } + __get_user(fcc, &fpu_ctx->fcc); + write_all_fcc(env, fcc); + __get_user(env->fcsr0, &fpu_ctx->fcsr); + restore_fp_status(env); + } +} + +/* + * Determine which stack to use. + */ +static abi_ptr get_sigframe(struct target_sigaction *ka, + CPULoongArchState *env, + struct extctx_layout *extctx) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), ka); + sp = ROUND_DOWN(sp, 16); + sp = setup_extcontext(extctx, sp); + sp -= sizeof(struct target_rt_sigframe); + + assert(QEMU_IS_ALIGNED(sp, 16)); + + return sp; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPULoongArchState *env) +{ + struct target_rt_sigframe *frame; + struct extctx_layout extctx; + abi_ptr frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, &extctx); + trace_user_setup_rt_frame(env, frame_addr); + + frame = lock_user(VERIFY_WRITE, frame_addr, + sizeof(*frame) + extctx.size, 0); + if (!frame) { + force_sigsegv(sig); + return; + } + 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); + + __put_user(0, &frame->rs_uc.tuc_flags); + __put_user(0, &frame->rs_uc.tuc_link); + target_save_altstack(&frame->rs_uc.tuc_stack, env); + + setup_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); + } + + env->gpr[4] = sig; + env->gpr[5] = frame_addr + offsetof(struct target_rt_sigframe, rs_info); + env->gpr[6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc); + env->gpr[3] = frame_addr; + env->gpr[1] = default_rt_sigreturn; + + env->pc = ka->_sa_handler; + unlock_user(frame, frame_addr, sizeof(*frame) + extctx.size); +} + +long do_rt_sigreturn(CPULoongArchState *env) +{ + struct target_rt_sigframe *frame; + struct extctx_layout extctx; + abi_ulong frame_addr; + sigset_t blocked; + + frame_addr = env->gpr[3]; + trace_user_do_rt_sigreturn(env, frame_addr); + + if (!parse_extcontext(&extctx, frame_addr + sizeof(*frame))) { + goto badframe; + } + + frame = lock_user(VERIFY_READ, frame_addr, + sizeof(*frame) + extctx.size, 1); + if (!frame) { + goto badframe; + } + if (extctx.fpu.gaddr) { + extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); + } + + target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); + set_sigmask(&blocked); + + restore_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx); + + target_restore_altstack(&frame->rs_uc.tuc_stack, env); + + unlock_user(frame, frame_addr, 0); + return -QEMU_ESIGRETURN; + + badframe: + force_sig(TARGET_SIGSEGV); + return -QEMU_ESIGRETURN; +} + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); + assert(tramp != NULL); + + __put_user(0x03822c0b, tramp + 0); /* ori a7, zero, 0x8b */ + __put_user(0x002b0000, tramp + 1); /* syscall 0 */ + + default_rt_sigreturn = sigtramp_page; + unlock_user(tramp, sigtramp_page, 8); +} diff --git a/linux-user/loongarch64/sockbits.h b/linux-user/loongarch64/sockbits.h new file mode 100644 index 00000000000..1cffcae120e --- /dev/null +++ b/linux-user/loongarch64/sockbits.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_SOCKBITS_H +#define LOONGARCH_TARGET_SOCKBITS_H + +#include "../generic/sockbits.h" + +#endif diff --git a/linux-user/loongarch64/syscall_nr.h b/linux-user/loongarch64/syscall_nr.h new file mode 100644 index 00000000000..be00915adf2 --- /dev/null +++ b/linux-user/loongarch64/syscall_nr.h @@ -0,0 +1,312 @@ +/* + * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh + */ +#ifndef LINUX_USER_LOONGARCH_SYSCALL_NR_H +#define LINUX_USER_LOONGARCH_SYSCALL_NR_H + +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs 43 +#define TARGET_NR_fstatfs 44 +#define TARGET_NR_truncate 45 +#define TARGET_NR_ftruncate 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_lseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap 222 +#define TARGET_NR_fadvise64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_io_pgetevents 292 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 +#define TARGET_NR_pidfd_send_signal 424 +#define TARGET_NR_io_uring_setup 425 +#define TARGET_NR_io_uring_enter 426 +#define TARGET_NR_io_uring_register 427 +#define TARGET_NR_open_tree 428 +#define TARGET_NR_move_mount 429 +#define TARGET_NR_fsopen 430 +#define TARGET_NR_fsconfig 431 +#define TARGET_NR_fsmount 432 +#define TARGET_NR_fspick 433 +#define TARGET_NR_pidfd_open 434 +#define TARGET_NR_clone3 435 +#define TARGET_NR_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_process_madvise 440 +#define TARGET_NR_epoll_pwait2 441 +#define TARGET_NR_mount_setattr 442 +#define TARGET_NR_quotactl_fd 443 +#define TARGET_NR_landlock_create_ruleset 444 +#define TARGET_NR_landlock_add_rule 445 +#define TARGET_NR_landlock_restrict_self 446 +#define TARGET_NR_process_mrelease 448 +#define TARGET_NR_futex_waitv 449 +#define TARGET_NR_set_mempolicy_home_node 450 +#define TARGET_NR_syscalls 451 + +#endif /* LINUX_USER_LOONGARCH_SYSCALL_NR_H */ diff --git a/linux-user/loongarch64/target_cpu.h b/linux-user/loongarch64/target_cpu.h new file mode 100644 index 00000000000..a29af661566 --- /dev/null +++ b/linux-user/loongarch64/target_cpu.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch specific CPU ABI and functions for linux-user + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_CPU_H +#define LOONGARCH_TARGET_CPU_H + +static inline void cpu_clone_regs_child(CPULoongArchState *env, + target_ulong newsp, unsigned flags) +{ + if (newsp) { + env->gpr[3] = newsp; + } + env->gpr[4] = 0; +} + +static inline void cpu_clone_regs_parent(CPULoongArchState *env, + unsigned flags) +{ +} + +static inline void cpu_set_tls(CPULoongArchState *env, target_ulong newtls) +{ + env->gpr[2] = newtls; +} + +static inline abi_ulong get_sp_from_cpustate(CPULoongArchState *state) +{ + return state->gpr[3]; +} +#endif diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h new file mode 100644 index 00000000000..95c3f05a46d --- /dev/null +++ b/linux-user/loongarch64/target_elf.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_ELF_H +#define LOONGARCH_TARGET_ELF_H +static inline const char *cpu_get_model(uint32_t eflags) +{ + return "la464"; +} +#endif diff --git a/linux-user/loongarch64/target_errno_defs.h b/linux-user/loongarch64/target_errno_defs.h new file mode 100644 index 00000000000..c198b8aca9d --- /dev/null +++ b/linux-user/loongarch64/target_errno_defs.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_ERRNO_DEFS_H +#define LOONGARCH_TARGET_ERRNO_DEFS_H + +/* Target uses generic errno */ +#include "../generic/target_errno_defs.h" + +#endif diff --git a/linux-user/loongarch64/target_fcntl.h b/linux-user/loongarch64/target_fcntl.h new file mode 100644 index 00000000000..99bf5868545 --- /dev/null +++ b/linux-user/loongarch64/target_fcntl.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_FCNTL_H +#define LOONGARCH_TARGET_FCNTL_H + +#include "../generic/fcntl.h" + +#endif diff --git a/linux-user/loongarch64/target_prctl.h b/linux-user/loongarch64/target_prctl.h new file mode 100644 index 00000000000..eb53b31ad55 --- /dev/null +++ b/linux-user/loongarch64/target_prctl.h @@ -0,0 +1 @@ +/* No special prctl support required. */ diff --git a/linux-user/loongarch64/target_resource.h b/linux-user/loongarch64/target_resource.h new file mode 100644 index 00000000000..0f86bf24ee9 --- /dev/null +++ b/linux-user/loongarch64/target_resource.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_RESOURCE_H +#define LOONGARCH_TARGET_RESOURCE_H + +#include "../generic/target_resource.h" + +#endif diff --git a/linux-user/loongarch64/target_signal.h b/linux-user/loongarch64/target_signal.h new file mode 100644 index 00000000000..ad3aaffcb42 --- /dev/null +++ b/linux-user/loongarch64/target_signal.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_SIGNAL_H +#define LOONGARCH_TARGET_SIGNAL_H + +#include "../generic/signal.h" + +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + +#endif /* LOONGARCH_TARGET_SIGNAL_H */ diff --git a/linux-user/loongarch64/target_structs.h b/linux-user/loongarch64/target_structs.h new file mode 100644 index 00000000000..6041441e15e --- /dev/null +++ b/linux-user/loongarch64/target_structs.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_STRUCTS_H +#define LOONGARCH_TARGET_STRUCTS_H + +#include "../generic/target_structs.h" + +#endif diff --git a/linux-user/loongarch64/target_syscall.h b/linux-user/loongarch64/target_syscall.h new file mode 100644 index 00000000000..8b5de521243 --- /dev/null +++ b/linux-user/loongarch64/target_syscall.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_SYSCALL_H +#define LOONGARCH_TARGET_SYSCALL_H + +#include "qemu/units.h" + +/* + * this struct defines the way the registers are stored on the + * stack during a system call. + */ + +struct target_pt_regs { + /* Saved main processor registers. */ + target_ulong regs[32]; + + /* Saved special registers. */ + struct { + target_ulong era; + target_ulong badv; + target_ulong crmd; + target_ulong prmd; + target_ulong euen; + target_ulong ecfg; + target_ulong estat; + } csr; + target_ulong orig_a0; + target_ulong __last[0]; +}; + +#define UNAME_MACHINE "loongarch64" +#define UNAME_MINIMUM_RELEASE "5.19.0" + +#define TARGET_MCL_CURRENT 1 +#define TARGET_MCL_FUTURE 2 +#define TARGET_MCL_ONFAULT 4 + +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPULoongArchState *env) +{ + return 64 * KiB; +} + +#endif diff --git a/linux-user/loongarch64/termbits.h b/linux-user/loongarch64/termbits.h new file mode 100644 index 00000000000..d425db8748c --- /dev/null +++ b/linux-user/loongarch64/termbits.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_TERMBITS_H +#define LOONGARCH_TARGET_TERMBITS_H + +#include "../generic/termbits.h" + +#endif diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 3d3033155f6..caead1cb741 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -36,11 +36,6 @@ void cpu_loop(CPUM68KState *env) process_queued_cpu_work(cs); switch(trapnr) { - case EXCP_HALT_INSN: - /* Semihosing syscall. */ - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - break; case EXCP_ILLEGAL: case EXCP_LINEA: case EXCP_LINEF: diff --git a/linux-user/main.c b/linux-user/main.c index eb33bb8b2c7..187b3f28b25 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -54,6 +54,10 @@ #include "loader.h" #include "user-mmap.h" +#ifdef CONFIG_SEMIHOSTING +#include "semihosting/semihost.h" +#endif + #ifndef AT_FLAGS_PRESERVE_ARGV0 #define AT_FLAGS_PRESERVE_ARGV0_BIT 0 #define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT) @@ -951,6 +955,11 @@ int main(int argc, char **argv, char **envp) } gdb_handlesig(cpu, 0); } + +#ifdef CONFIG_SEMIHOSTING + qemu_semihosting_guestfd_init(); +#endif + // cpu_loop(env); //// --- Begin LibAFL code --- diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 48e13737962..4e7a6be6ee1 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -835,3 +835,67 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, mmap_unlock(); return new_addr; } + +static bool can_passthrough_madv_dontneed(abi_ulong start, abi_ulong end) +{ + ulong addr; + + if ((start | end) & ~qemu_host_page_mask) { + return false; + } + + for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { + if (!(page_get_flags(addr) & PAGE_ANON)) { + return false; + } + } + + return true; +} + +abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) +{ + abi_ulong len, end; + int ret = 0; + + if (start & ~TARGET_PAGE_MASK) { + return -TARGET_EINVAL; + } + len = TARGET_PAGE_ALIGN(len_in); + + if (len_in && !len) { + return -TARGET_EINVAL; + } + + end = start + len; + if (end < start) { + return -TARGET_EINVAL; + } + + if (end == start) { + return 0; + } + + if (!guest_range_valid_untagged(start, len)) { + return -TARGET_EINVAL; + } + + /* + * A straight passthrough may not be safe because qemu sometimes turns + * private file-backed mappings into anonymous mappings. + * + * This is a hint, so ignoring and returning success is ok. + * + * This breaks MADV_DONTNEED, completely implementing which is quite + * complicated. However, there is one low-hanging fruit: host-page-aligned + * anonymous mappings. In this case passthrough is safe, so do it. + */ + mmap_lock(); + if ((advice & MADV_DONTNEED) && + can_passthrough_madv_dontneed(start, end)) { + ret = get_errno(madvise(g2h_untagged(start), len, MADV_DONTNEED)); + } + mmap_unlock(); + + return ret; +} diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 29084c14219..bffca7db127 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -81,7 +81,7 @@ void cpu_loop(CPURISCVState *env) force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case RISCV_EXCP_SEMIHOST: - env->gpr[xA0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; break; default: diff --git a/linux-user/semihost.c b/linux-user/semihost.c index 17f074ac565..cee62a365c4 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -16,39 +16,6 @@ #include "user-internals.h" #include -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) -{ - int len = target_strlen(addr); - void *s; - if (len < 0){ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - return 0; - } - s = lock_user(VERIFY_READ, addr, (long)(len + 1), 1); - g_assert(s); /* target_strlen has already verified this will work */ - len = write(STDERR_FILENO, s, len); - unlock_user(s, addr, 0); - return len; -} - -void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) -{ - char c; - - if (get_user_u8(c, addr)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - } else { - if (write(STDERR_FILENO, &c, 1) != 1) { - qemu_log_mask(LOG_UNIMP, "%s: unexpected write to stdout failure", - __func__); - } - } -} - /* * For linux-user we can safely block. However as we want to return as * soon as a character is read we need to tweak the termio to disable @@ -56,21 +23,28 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) * program is expecting more normal behaviour. This is slow but * nothing using semihosting console reading is expecting to be fast. */ -target_ulong qemu_semihosting_console_inc(CPUArchState *env) +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) { - uint8_t c; + int ret; struct termios old_tio, new_tio; /* Disable line-buffering and echo */ tcgetattr(STDIN_FILENO, &old_tio); new_tio = old_tio; new_tio.c_lflag &= (~ICANON & ~ECHO); + new_tio.c_cc[VMIN] = 1; + new_tio.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); - c = getchar(); + ret = fread(buf, 1, len, stdin); /* restore config */ tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); - return (target_ulong) c; + return ret; +} + +int qemu_semihosting_console_write(void *buf, int len) +{ + return fwrite(buf, 1, len, stderr); } diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 028a68ea6d7..da4e5aaadce 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -320,9 +320,6 @@ _syscall3(int,sys_syslog,int,type,char*,bufp,int,len) #ifdef __NR_exit_group _syscall1(int,exit_group,int,error_code) #endif -#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) -_syscall1(int,set_tid_address,int *,tidptr) -#endif #if defined(__NR_futex) _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, const struct timespec *,timeout,int *,uaddr2,int,val3) @@ -538,7 +535,7 @@ static inline int target_to_host_errno(int target_errno) } } -static inline abi_long get_errno(abi_long ret) +abi_long get_errno(abi_long ret) { if (ret == -1) return -host_to_target_errno(errno); @@ -6363,6 +6360,12 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) #ifndef PR_SET_SYSCALL_USER_DISPATCH # define PR_SET_SYSCALL_USER_DISPATCH 59 #endif +#ifndef PR_SME_SET_VL +# define PR_SME_SET_VL 63 +# define PR_SME_GET_VL 64 +# define PR_SME_VL_LEN_MASK 0xffff +# define PR_SME_VL_INHERIT (1 << 17) +#endif #include "target_prctl.h" @@ -6382,11 +6385,11 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2) #ifndef do_prctl_set_fp_mode #define do_prctl_set_fp_mode do_prctl_inval1 #endif -#ifndef do_prctl_get_vl -#define do_prctl_get_vl do_prctl_inval0 +#ifndef do_prctl_sve_get_vl +#define do_prctl_sve_get_vl do_prctl_inval0 #endif -#ifndef do_prctl_set_vl -#define do_prctl_set_vl do_prctl_inval1 +#ifndef do_prctl_sve_set_vl +#define do_prctl_sve_set_vl do_prctl_inval1 #endif #ifndef do_prctl_reset_keys #define do_prctl_reset_keys do_prctl_inval1 @@ -6403,6 +6406,12 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2) #ifndef do_prctl_set_unalign #define do_prctl_set_unalign do_prctl_inval1 #endif +#ifndef do_prctl_sme_get_vl +#define do_prctl_sme_get_vl do_prctl_inval0 +#endif +#ifndef do_prctl_sme_set_vl +#define do_prctl_sme_set_vl do_prctl_inval1 +#endif static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) @@ -6451,9 +6460,13 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_SET_FP_MODE: return do_prctl_set_fp_mode(env, arg2); case PR_SVE_GET_VL: - return do_prctl_get_vl(env); + return do_prctl_sve_get_vl(env); case PR_SVE_SET_VL: - return do_prctl_set_vl(env, arg2); + return do_prctl_sve_set_vl(env, arg2); + case PR_SME_GET_VL: + return do_prctl_sme_get_vl(env); + case PR_SME_SET_VL: + return do_prctl_sme_set_vl(env, arg2); case PR_PAC_RESET_KEYS: if (arg3 || arg4 || arg5) { return -TARGET_EINVAL; @@ -11843,11 +11856,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_madvise case TARGET_NR_madvise: - /* A straight passthrough may not be safe because qemu sometimes - turns private file-backed mappings into anonymous mappings. - This will break MADV_DONTNEED. - This is a hint, so ignoring and returning success is ok. */ - return 0; + return target_madvise(arg1, arg2, arg3); #endif #ifdef TARGET_NR_fcntl64 case TARGET_NR_fcntl64: @@ -12236,9 +12245,14 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } #endif -#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) +#if defined(TARGET_NR_set_tid_address) case TARGET_NR_set_tid_address: - return get_errno(set_tid_address((int *)g2h(cpu, arg1))); + { + TaskState *ts = cpu->opaque; + ts->child_tidptr = arg1; + /* do not call host set_tid_address() syscall, instead return tid() */ + return get_errno(sys_gettid()); + } #endif case TARGET_NR_tkill: diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 4587b62ac97..85b0f33e91d 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -74,7 +74,7 @@ || defined(TARGET_M68K) || defined(TARGET_CRIS) \ || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \ - || defined(TARGET_XTENSA) + || defined(TARGET_XTENSA) || defined(TARGET_LOONGARCH64) #define TARGET_IOC_SIZEBITS 14 #define TARGET_IOC_DIRBITS 2 @@ -2196,6 +2196,10 @@ struct target_stat64 { uint64_t st_ino; }; +#elif defined(TARGET_LOONGARCH64) + +/* LoongArch no newfstatat/fstat syscall. */ + #else #error unsupported CPU #endif diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 5e86e5a40b5..b578cabdfb2 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -65,6 +65,7 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, abi_long arg8); extern __thread CPUState *thread_cpu; 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); diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h index d1dec99c024..480ce1c1148 100644 --- a/linux-user/user-mmap.h +++ b/linux-user/user-mmap.h @@ -25,6 +25,7 @@ int target_munmap(abi_ulong start, abi_ulong len); abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, abi_ulong new_size, unsigned long flags, abi_ulong new_addr); +abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice); extern unsigned long last_brk; extern abi_ulong mmap_next_start; abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong); diff --git a/meson.build b/meson.build index 86472101f42..29a780721df 100644 --- a/meson.build +++ b/meson.build @@ -7,6 +7,8 @@ add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) +meson.add_postconf_script(find_program('scripts/symlink-install-tree.py')) + not_found = dependency('', required: false) keyval = import('keyval') ss = import('sourceset') @@ -209,9 +211,14 @@ if get_option('fuzzing') configure_file(output: 'instrumentation-filter', input: 'scripts/oss-fuzz/instrumentation-filter-template', copy: true) - add_global_arguments( - cc.get_supported_arguments('-fsanitize-coverage-allowlist=instrumentation-filter'), - native: false, language: ['c', 'cpp', 'objc']) + + if cc.compiles('int main () { return 0; }', + name: '-fsanitize-coverage-allowlist=/dev/null', + args: ['-fsanitize-coverage-allowlist=/dev/null', + '-fsanitize-coverage=trace-pc'] ) + add_global_arguments('-fsanitize-coverage-allowlist=instrumentation-filter', + native: false, language: ['c', 'cpp', 'objc']) + endif if get_option('fuzzing_engine') == '' # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the @@ -246,7 +253,6 @@ endif add_project_arguments('-iquote', '.', '-iquote', meson.current_source_dir(), '-iquote', meson.current_source_dir() / 'include', - '-iquote', meson.current_source_dir() / 'disas/libvixl', language: ['c', 'cpp', 'objc']) link_language = meson.get_external_property('link_language', 'cpp') @@ -352,10 +358,12 @@ nvmm =not_found hvf = not_found midl = not_found widl = not_found +pathcch = not_found host_dsosuf = '.so' if targetos == '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') @@ -579,7 +587,8 @@ if get_option('attr').allowed() endif endif -cocoa = dependency('appleframeworks', modules: 'Cocoa', required: get_option('cocoa')) +cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'], + required: get_option('cocoa')) if cocoa.found() and get_option('sdl').enabled() error('Cocoa and SDL cannot be enabled at the same time') endif @@ -1206,7 +1215,7 @@ if gtkx11.found() endif png = not_found if get_option('png').allowed() and have_system - png = dependency('libpng', required: get_option('png'), + png = dependency('libpng', version: '>=1.6.34', required: get_option('png'), method: 'pkg-config', kwargs: static_kwargs) endif vnc = not_found @@ -1512,7 +1521,7 @@ have_vhost_user_blk_server = get_option('vhost_user_blk_server') \ error_message: 'vhost_user_blk_server requires linux') \ .require(have_vhost_user, error_message: 'vhost_user_blk_server requires vhost-user support') \ - .disable_auto_if(not have_system) \ + .disable_auto_if(not have_tools and not have_system) \ .allowed() if get_option('fuse').disabled() and get_option('fuse_lseek').enabled() @@ -1537,6 +1546,26 @@ if get_option('fuse_lseek').allowed() endif endif +have_libvduse = (targetos == 'linux') +if get_option('libvduse').enabled() + if targetos != 'linux' + error('libvduse requires linux') + endif +elif get_option('libvduse').disabled() + have_libvduse = false +endif + +have_vduse_blk_export = (have_libvduse and targetos == 'linux') +if get_option('vduse_blk_export').enabled() + if targetos != 'linux' + error('vduse_blk_export requires linux') + elif not have_libvduse + error('vduse_blk_export requires libvduse support') + endif +elif get_option('vduse_blk_export').disabled() + have_vduse_blk_export = false +endif + # libbpf libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config') if libbpf.found() and not cc.links(''' @@ -1648,6 +1677,8 @@ dbus_display = get_option('dbus_display') \ error_message: '-display dbus requires --enable-modules') \ .require(gdbus_codegen.found(), error_message: '-display dbus requires gdbus-codegen') \ + .require(opengl.found(), + error_message: '-display dbus requires epoxy/egl') \ .allowed() have_virtfs = get_option('virtfs') \ @@ -1688,7 +1719,13 @@ config_host_data.set_quoted('CONFIG_PREFIX', get_option('prefix')) config_host_data.set_quoted('CONFIG_QEMU_CONFDIR', get_option('prefix') / qemu_confdir) config_host_data.set_quoted('CONFIG_QEMU_DATADIR', get_option('prefix') / qemu_datadir) config_host_data.set_quoted('CONFIG_QEMU_DESKTOPDIR', get_option('prefix') / qemu_desktopdir) -config_host_data.set_quoted('CONFIG_QEMU_FIRMWAREPATH', get_option('prefix') / get_option('qemu_firmwarepath')) + +qemu_firmwarepath = '' +foreach k : get_option('qemu_firmwarepath') + qemu_firmwarepath += '"' + get_option('prefix') / k + '", ' +endforeach +config_host_data.set('CONFIG_QEMU_FIRMWAREPATH', qemu_firmwarepath) + config_host_data.set_quoted('CONFIG_QEMU_HELPERDIR', get_option('prefix') / get_option('libexecdir')) config_host_data.set_quoted('CONFIG_QEMU_ICONDIR', get_option('prefix') / qemu_icondir) config_host_data.set_quoted('CONFIG_QEMU_LOCALEDIR', get_option('prefix') / get_option('localedir')) @@ -1779,6 +1816,7 @@ config_host_data.set('CONFIG_VHOST_CRYPTO', have_vhost_user_crypto) config_host_data.set('CONFIG_VHOST_VDPA', have_vhost_vdpa) config_host_data.set('CONFIG_VMNET', vmnet.found()) config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server) +config_host_data.set('CONFIG_VDUSE_BLK_EXPORT', have_vduse_blk_export) config_host_data.set('CONFIG_PNG', png.found()) config_host_data.set('CONFIG_VNC', vnc.found()) config_host_data.set('CONFIG_VNC_JPEG', jpeg.found()) @@ -1878,6 +1916,12 @@ config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul')) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) +if rbd.found() + config_host_data.set('HAVE_RBD_NAMESPACE_EXISTS', + cc.has_function('rbd_namespace_exists', + dependencies: rbd, + prefix: '#include ')) +endif if rdma.found() config_host_data.set('HAVE_IBV_ADVISE_MR', cc.has_function('ibv_advise_mr', @@ -2341,7 +2385,6 @@ config_target_mak = {} disassemblers = { 'alpha' : ['CONFIG_ALPHA_DIS'], - 'arm' : ['CONFIG_ARM_DIS'], 'avr' : ['CONFIG_AVR_DIS'], 'cris' : ['CONFIG_CRIS_DIS'], 'hexagon' : ['CONFIG_HEXAGON_DIS'], @@ -2364,8 +2407,6 @@ disassemblers = { } if link_language == 'cpp' disassemblers += { - 'aarch64' : [ 'CONFIG_ARM_A64_DIS'], - 'arm' : [ 'CONFIG_ARM_DIS', 'CONFIG_ARM_A64_DIS'], 'mips' : [ 'CONFIG_MIPS_DIS', 'CONFIG_NANOMIPS_DIS'], } endif @@ -2984,6 +3025,12 @@ if targetos == 'linux' and have_vhost_user vhost_user = libvhost_user.get_variable('vhost_user_dep') endif +libvduse = not_found +if have_libvduse + libvduse_proj = subproject('libvduse') + libvduse = libvduse_proj.get_variable('libvduse_dep') +endif + # NOTE: the trace/ subdirectory needs the qapi_trace_events variable # that is filled in by qapi/. subdir('qapi') @@ -3679,7 +3726,8 @@ endif summary_info = {} summary_info += {'Install prefix': get_option('prefix')} summary_info += {'BIOS directory': qemu_datadir} -summary_info += {'firmware path': get_option('prefix') / get_option('qemu_firmwarepath')} +pathsep = targetos == 'windows' ? ';' : ':' +summary_info += {'firmware path': pathsep.join(get_option('qemu_firmwarepath'))} summary_info += {'binary directory': get_option('prefix') / get_option('bindir')} summary_info += {'library directory': get_option('prefix') / get_option('libdir')} summary_info += {'module directory': qemu_moddir} @@ -3803,21 +3851,24 @@ endif summary_info += {'strip binaries': get_option('strip')} summary_info += {'sparse': sparse} summary_info += {'mingw32 support': targetos == 'windows'} +summary(summary_info, bool_yn: true, section: 'Compilation') # snarf the cross-compilation information for tests +summary_info = {} +have_cross = false foreach target: target_dirs tcg_mak = meson.current_build_dir() / 'tests/tcg' / 'config-' + target + '.mak' if fs.exists(tcg_mak) config_cross_tcg = keyval.load(tcg_mak) - target = config_cross_tcg['TARGET_NAME'] - compiler = '' if 'CC' in config_cross_tcg - summary_info += {target + ' tests': config_cross_tcg['CC']} + summary_info += {config_cross_tcg['TARGET_NAME']: config_cross_tcg['CC']} + have_cross = true endif - endif + endif endforeach - -summary(summary_info, bool_yn: true, section: 'Compilation') +if have_cross + summary(summary_info, bool_yn: true, section: 'Cross compilers') +endif # Targets and accelerators summary_info = {} @@ -3871,6 +3922,7 @@ if have_block summary_info += {'qed support': get_option('qed').allowed()} summary_info += {'parallels support': get_option('parallels').allowed()} summary_info += {'FUSE exports': fuse} + summary_info += {'VDUSE block exports': have_vduse_blk_export} endif summary(summary_info, bool_yn: true, section: 'Block layer support') diff --git a/meson_options.txt b/meson_options.txt index f3e2f22c1ed..e58e158396b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,7 +6,7 @@ option('qemu_suffix', type : 'string', value: 'qemu', description: 'Suffix for QEMU data/modules/config directories (can be empty)') option('docdir', type : 'string', value : 'share/doc', description: 'Base directory for documentation installation (can be empty)') -option('qemu_firmwarepath', type : 'string', value : 'qemu-firmware', +option('qemu_firmwarepath', type : 'array', value : ['share/qemu-firmware'], description: 'search PATH for firmware files') option('pkgversion', type : 'string', value : '', description: 'use specified string as sub-version of the package') @@ -257,6 +257,10 @@ option('virtfs', type: 'feature', value: 'auto', description: 'virtio-9p support') option('virtiofsd', type: 'feature', value: 'auto', description: 'build virtiofs daemon (virtiofsd)') +option('libvduse', type: 'feature', value: 'auto', + description: 'build VDUSE Library') +option('vduse_blk_export', type: 'feature', value: 'auto', + description: 'VDUSE block export support') option('capstone', type: 'feature', value: 'auto', description: 'Whether and how to find the capstone library') diff --git a/migration/block.c b/migration/block.c index 077a4133258..9e5aae58982 100644 --- a/migration/block.c +++ b/migration/block.c @@ -568,8 +568,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); blk_mig_unlock(); } else { - ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE, blk->buf, - nr_sectors * BDRV_SECTOR_SIZE); + ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE, + nr_sectors * BDRV_SECTOR_SIZE, blk->buf, 0); if (ret < 0) { goto error; } @@ -756,8 +756,8 @@ static int block_save_setup(QEMUFile *f, void *opaque) static int block_save_iterate(QEMUFile *f, void *opaque) { int ret; - int64_t last_ftell = qemu_ftell(f); - int64_t delta_ftell; + int64_t last_bytes = qemu_file_total_transferred(f); + int64_t delta_bytes; trace_migration_block_save("iterate", block_mig_state.submitted, block_mig_state.transferred); @@ -809,10 +809,10 @@ static int block_save_iterate(QEMUFile *f, void *opaque) } qemu_put_be64(f, BLK_MIG_FLAG_EOS); - delta_ftell = qemu_ftell(f) - last_ftell; - if (delta_ftell > 0) { + delta_bytes = qemu_file_total_transferred(f) - last_bytes; + if (delta_bytes > 0) { return 1; - } else if (delta_ftell < 0) { + } else if (delta_bytes < 0) { return -1; } else { return 0; @@ -976,8 +976,8 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) cluster_size, BDRV_REQ_MAY_UNMAP); } else { - ret = blk_pwrite(blk, cur_addr, cur_buf, - cluster_size, 0); + ret = blk_pwrite(blk, cur_addr, cluster_size, cur_buf, + 0); } if (ret < 0) { break; diff --git a/migration/channel-block.c b/migration/channel-block.c new file mode 100644 index 00000000000..c55c8c93ce4 --- /dev/null +++ b/migration/channel-block.c @@ -0,0 +1,195 @@ +/* + * QEMU I/O channels block driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "migration/channel-block.h" +#include "qapi/error.h" +#include "block/block.h" +#include "trace.h" + +QIOChannelBlock * +qio_channel_block_new(BlockDriverState *bs) +{ + QIOChannelBlock *ioc; + + ioc = QIO_CHANNEL_BLOCK(object_new(TYPE_QIO_CHANNEL_BLOCK)); + + bdrv_ref(bs); + ioc->bs = bs; + + return ioc; +} + + +static void +qio_channel_block_finalize(Object *obj) +{ + QIOChannelBlock *ioc = QIO_CHANNEL_BLOCK(obj); + + g_clear_pointer(&ioc->bs, bdrv_unref); +} + + +static ssize_t +qio_channel_block_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + ret = bdrv_readv_vmstate(bioc->bs, &qiov, bioc->offset); + if (ret < 0) { + return ret; + } + + bioc->offset += qiov.size; + return qiov.size; +} + + +static ssize_t +qio_channel_block_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + int flags, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + ret = bdrv_writev_vmstate(bioc->bs, &qiov, bioc->offset); + if (ret < 0) { + return ret; + } + + bioc->offset += qiov.size; + return qiov.size; +} + + +static int +qio_channel_block_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + if (!enabled) { + error_setg(errp, "Non-blocking mode not supported for block devices"); + return -1; + } + return 0; +} + + +static off_t +qio_channel_block_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + + switch (whence) { + case SEEK_SET: + bioc->offset = offset; + break; + case SEEK_CUR: + bioc->offset += whence; + break; + case SEEK_END: + error_setg(errp, "Size of VMstate region is unknown"); + return (off_t)-1; + default: + g_assert_not_reached(); + } + + return bioc->offset; +} + + +static int +qio_channel_block_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + int rv = bdrv_flush(bioc->bs); + + if (rv < 0) { + error_setg_errno(errp, -rv, + "Unable to flush VMState"); + return -1; + } + + g_clear_pointer(&bioc->bs, bdrv_unref); + bioc->offset = 0; + + return 0; +} + + +static void +qio_channel_block_set_aio_fd_handler(QIOChannel *ioc, + AioContext *ctx, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + /* XXX anything we can do here ? */ +} + + +static void +qio_channel_block_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_block_writev; + ioc_klass->io_readv = qio_channel_block_readv; + ioc_klass->io_set_blocking = qio_channel_block_set_blocking; + ioc_klass->io_seek = qio_channel_block_seek; + ioc_klass->io_close = qio_channel_block_close; + ioc_klass->io_set_aio_fd_handler = qio_channel_block_set_aio_fd_handler; +} + +static const TypeInfo qio_channel_block_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_BLOCK, + .instance_size = sizeof(QIOChannelBlock), + .instance_finalize = qio_channel_block_finalize, + .class_init = qio_channel_block_class_init, +}; + +static void +qio_channel_block_register_types(void) +{ + type_register_static(&qio_channel_block_info); +} + +type_init(qio_channel_block_register_types); diff --git a/migration/channel-block.h b/migration/channel-block.h new file mode 100644 index 00000000000..31673824e69 --- /dev/null +++ b/migration/channel-block.h @@ -0,0 +1,59 @@ +/* + * QEMU I/O channels block driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef QIO_CHANNEL_BLOCK_H +#define QIO_CHANNEL_BLOCK_H + +#include "io/channel.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL_BLOCK "qio-channel-block" +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelBlock, QIO_CHANNEL_BLOCK) + + +/** + * QIOChannelBlock: + * + * The QIOChannelBlock object provides a channel implementation + * that is able to perform I/O on the BlockDriverState objects + * to the VMState region. + */ + +struct QIOChannelBlock { + QIOChannel parent; + BlockDriverState *bs; + off_t offset; +}; + + +/** + * qio_channel_block_new: + * @bs: the block driver state + * + * Create a new IO channel object that can perform + * I/O on a BlockDriverState object to the VMState + * region + * + * Returns: the new channel object + */ +QIOChannelBlock * +qio_channel_block_new(BlockDriverState *bs); + +#endif /* QIO_CHANNEL_BLOCK_H */ diff --git a/migration/channel.c b/migration/channel.c index a162d00fea7..1b0815039f9 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -14,7 +14,7 @@ #include "channel.h" #include "tls.h" #include "migration.h" -#include "qemu-file-channel.h" +#include "qemu-file.h" #include "trace.h" #include "qapi/error.h" #include "io/channel-tls.h" @@ -38,9 +38,7 @@ void migration_channel_process_incoming(QIOChannel *ioc) trace_migration_set_incoming_channel( ioc, object_get_typename(OBJECT(ioc))); - if (migrate_use_tls() && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { + if (migrate_channel_requires_tls_upgrade(ioc)) { migration_tls_channel_process_incoming(s, ioc, &local_err); } else { migration_ioc_register_yank(ioc); @@ -70,10 +68,7 @@ void migration_channel_connect(MigrationState *s, ioc, object_get_typename(OBJECT(ioc)), hostname, error); if (!error) { - if (s->parameters.tls_creds && - *s->parameters.tls_creds && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { + if (migrate_channel_requires_tls_upgrade(ioc)) { migration_tls_channel_connect(s, ioc, hostname, &error); if (!error) { @@ -85,7 +80,7 @@ void migration_channel_connect(MigrationState *s, return; } } else { - QEMUFile *f = qemu_fopen_channel_output(ioc); + QEMUFile *f = qemu_file_new_output(ioc); migration_ioc_register_yank(ioc); diff --git a/migration/colo.c b/migration/colo.c index 5f7071b3cd6..2b71722fd65 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -14,7 +14,6 @@ #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qemu-file-channel.h" #include "migration.h" #include "qemu-file.h" #include "savevm.h" @@ -559,7 +558,7 @@ static void colo_process_checkpoint(MigrationState *s) goto out; } bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); - fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc)); + fb = qemu_file_new_output(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); qemu_mutex_lock_iothread(); @@ -873,7 +872,7 @@ void *colo_process_incoming_thread(void *opaque) colo_incoming_start_dirty_log(); bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); - fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); + fb = qemu_file_new_input(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); qemu_mutex_lock_iothread(); diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index aace12a7876..795fab5c374 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -46,7 +46,7 @@ static struct DirtyRateStat DirtyStat; static DirtyRateMeasureMode dirtyrate_mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; -static int64_t set_sample_page_period(int64_t msec, int64_t initial_time) +static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time) { int64_t current_time; @@ -60,6 +60,132 @@ static int64_t set_sample_page_period(int64_t msec, int64_t initial_time) return msec; } +static inline void record_dirtypages(DirtyPageRecord *dirty_pages, + CPUState *cpu, bool start) +{ + if (start) { + dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages; + } else { + dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages; + } +} + +static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, + int64_t calc_time_ms) +{ + uint64_t memory_size_MB; + uint64_t increased_dirty_pages = + dirty_pages.end_pages - dirty_pages.start_pages; + + memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; + + return memory_size_MB * 1000 / calc_time_ms; +} + +void global_dirty_log_change(unsigned int flag, bool start) +{ + qemu_mutex_lock_iothread(); + if (start) { + memory_global_dirty_log_start(flag); + } else { + memory_global_dirty_log_stop(flag); + } + qemu_mutex_unlock_iothread(); +} + +/* + * global_dirty_log_sync + * 1. sync dirty log from kvm + * 2. stop dirty tracking if needed. + */ +static void global_dirty_log_sync(unsigned int flag, bool one_shot) +{ + qemu_mutex_lock_iothread(); + memory_global_dirty_log_sync(); + if (one_shot) { + memory_global_dirty_log_stop(flag); + } + qemu_mutex_unlock_iothread(); +} + +static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) +{ + CPUState *cpu; + DirtyPageRecord *records; + int nvcpu = 0; + + CPU_FOREACH(cpu) { + nvcpu++; + } + + stat->nvcpu = nvcpu; + stat->rates = g_malloc0(sizeof(DirtyRateVcpu) * nvcpu); + + records = g_malloc0(sizeof(DirtyPageRecord) * nvcpu); + + return records; +} + +static void vcpu_dirty_stat_collect(VcpuStat *stat, + DirtyPageRecord *records, + bool start) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + record_dirtypages(records, cpu, start); + } +} + +int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, + VcpuStat *stat, + unsigned int flag, + bool one_shot) +{ + DirtyPageRecord *records; + int64_t init_time_ms; + int64_t duration; + int64_t dirtyrate; + int i = 0; + unsigned int gen_id; + +retry: + init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + cpu_list_lock(); + gen_id = cpu_list_generation_id_get(); + records = vcpu_dirty_stat_alloc(stat); + vcpu_dirty_stat_collect(stat, records, true); + cpu_list_unlock(); + + duration = dirty_stat_wait(calc_time_ms, init_time_ms); + + global_dirty_log_sync(flag, one_shot); + + cpu_list_lock(); + if (gen_id != cpu_list_generation_id_get()) { + g_free(records); + g_free(stat->rates); + cpu_list_unlock(); + goto retry; + } + vcpu_dirty_stat_collect(stat, records, false); + cpu_list_unlock(); + + for (i = 0; i < stat->nvcpu; i++) { + dirtyrate = do_calculate_dirtyrate(records[i], duration); + + stat->rates[i].id = i; + stat->rates[i].dirty_rate = dirtyrate; + + trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); + } + + g_free(records); + + return duration; +} + static bool is_sample_period_valid(int64_t sec) { if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC || @@ -396,44 +522,6 @@ static bool compare_page_hash_info(struct RamblockDirtyInfo *info, return true; } -static inline void record_dirtypages(DirtyPageRecord *dirty_pages, - CPUState *cpu, bool start) -{ - if (start) { - dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages; - } else { - dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages; - } -} - -static void dirtyrate_global_dirty_log_start(void) -{ - qemu_mutex_lock_iothread(); - memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); - qemu_mutex_unlock_iothread(); -} - -static void dirtyrate_global_dirty_log_stop(void) -{ - qemu_mutex_lock_iothread(); - memory_global_dirty_log_sync(); - memory_global_dirty_log_stop(GLOBAL_DIRTY_DIRTY_RATE); - qemu_mutex_unlock_iothread(); -} - -static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages) -{ - uint64_t memory_size_MB; - int64_t time_s; - uint64_t increased_dirty_pages = - dirty_pages.end_pages - dirty_pages.start_pages; - - memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; - time_s = DirtyStat.calc_time; - - return memory_size_MB / time_s; -} - static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, bool start) { @@ -444,11 +532,6 @@ static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, } } -static void do_calculate_dirtyrate_bitmap(DirtyPageRecord dirty_pages) -{ - DirtyStat.dirty_rate = do_calculate_dirtyrate_vcpu(dirty_pages); -} - static inline void dirtyrate_manual_reset_protect(void) { RAMBlock *block = NULL; @@ -492,71 +575,49 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) DirtyStat.start_time = start_time / 1000; msec = config.sample_period_seconds * 1000; - msec = set_sample_page_period(msec, start_time); + msec = dirty_stat_wait(msec, start_time); DirtyStat.calc_time = msec / 1000; /* - * dirtyrate_global_dirty_log_stop do two things. + * do two things. * 1. fetch dirty bitmap from kvm * 2. stop dirty tracking */ - dirtyrate_global_dirty_log_stop(); + global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true); record_dirtypages_bitmap(&dirty_pages, false); - do_calculate_dirtyrate_bitmap(dirty_pages); + DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, msec); } static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) { - CPUState *cpu; - int64_t msec = 0; - int64_t start_time; + int64_t duration; uint64_t dirtyrate = 0; uint64_t dirtyrate_sum = 0; - DirtyPageRecord *dirty_pages; - int nvcpu = 0; int i = 0; - CPU_FOREACH(cpu) { - nvcpu++; - } - - dirty_pages = malloc(sizeof(*dirty_pages) * nvcpu); - - DirtyStat.dirty_ring.nvcpu = nvcpu; - DirtyStat.dirty_ring.rates = malloc(sizeof(DirtyRateVcpu) * nvcpu); - - dirtyrate_global_dirty_log_start(); - - CPU_FOREACH(cpu) { - record_dirtypages(dirty_pages, cpu, true); - } - - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - DirtyStat.start_time = start_time / 1000; + /* start log sync */ + global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true); - msec = config.sample_period_seconds * 1000; - msec = set_sample_page_period(msec, start_time); - DirtyStat.calc_time = msec / 1000; + DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; - dirtyrate_global_dirty_log_stop(); + /* calculate vcpu dirtyrate */ + duration = vcpu_calculate_dirtyrate(config.sample_period_seconds * 1000, + &DirtyStat.dirty_ring, + GLOBAL_DIRTY_DIRTY_RATE, + true); - CPU_FOREACH(cpu) { - record_dirtypages(dirty_pages, cpu, false); - } + DirtyStat.calc_time = duration / 1000; + /* calculate vm dirtyrate */ for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { - dirtyrate = do_calculate_dirtyrate_vcpu(dirty_pages[i]); - trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); - - DirtyStat.dirty_ring.rates[i].id = i; + dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate; DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate; dirtyrate_sum += dirtyrate; } DirtyStat.dirty_rate = dirtyrate_sum; - free(dirty_pages); } static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config) @@ -574,7 +635,7 @@ static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config) rcu_read_unlock(); msec = config.sample_period_seconds * 1000; - msec = set_sample_page_period(msec, initial_time); + msec = dirty_stat_wait(msec, initial_time); DirtyStat.start_time = initial_time / 1000; DirtyStat.calc_time = msec / 1000; diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h index 69d4c5b8655..594a5c0bb64 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -13,6 +13,8 @@ #ifndef QEMU_MIGRATION_DIRTYRATE_H #define QEMU_MIGRATION_DIRTYRATE_H +#include "sysemu/dirtyrate.h" + /* * Sample 512 pages per GB as default. */ @@ -65,11 +67,6 @@ typedef struct SampleVMStat { uint64_t total_block_mem_MB; /* size of total sampled pages in MB */ } SampleVMStat; -typedef struct VcpuStat { - int nvcpu; /* number of vcpu */ - DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ -} VcpuStat; - /* * Store calculation statistics for each measure. */ diff --git a/migration/meson.build b/migration/meson.build index 6880b61b108..690487cf1a8 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -4,7 +4,6 @@ migration_files = files( 'xbzrle.c', 'vmstate-types.c', 'vmstate.c', - 'qemu-file-channel.c', 'qemu-file.c', 'yank_functions.c', ) @@ -13,6 +12,7 @@ softmmu_ss.add(migration_files) softmmu_ss.add(files( 'block-dirty-bitmap.c', 'channel.c', + 'channel-block.c', 'colo-failover.c', 'colo.c', 'exec.c', diff --git a/migration/migration.c b/migration/migration.c index 31739b2af92..e03f698a3ca 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -30,7 +30,6 @@ #include "migration/misc.h" #include "migration.h" #include "savevm.h" -#include "qemu-file-channel.h" #include "qemu-file.h" #include "migration/vmstate.h" #include "block/block.h" @@ -49,6 +48,7 @@ #include "trace.h" #include "exec/target_page.h" #include "io/channel-buffer.h" +#include "io/channel-tls.h" #include "migration/colo.h" #include "hw/boards.h" #include "hw/qdev-properties.h" @@ -163,7 +163,8 @@ INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, MIGRATION_CAPABILITY_COMPRESS, MIGRATION_CAPABILITY_XBZRLE, MIGRATION_CAPABILITY_X_COLO, - MIGRATION_CAPABILITY_VALIDATE_UUID); + MIGRATION_CAPABILITY_VALIDATE_UUID, + MIGRATION_CAPABILITY_ZERO_COPY_SEND); /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add @@ -215,9 +216,11 @@ void migration_object_init(void) current_incoming->postcopy_remote_fds = g_array_new(FALSE, TRUE, sizeof(struct PostCopyFD)); qemu_mutex_init(¤t_incoming->rp_mutex); + qemu_mutex_init(¤t_incoming->postcopy_prio_thread_mutex); qemu_event_init(¤t_incoming->main_thread_load_event, false); qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0); qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0); + qemu_sem_init(¤t_incoming->postcopy_pause_sem_fast_load, 0); qemu_mutex_init(¤t_incoming->page_request_mutex); current_incoming->page_requested = g_tree_new(page_request_addr_cmp); @@ -321,6 +324,12 @@ void migration_incoming_state_destroy(void) mis->page_requested = NULL; } + if (mis->postcopy_qemufile_dst) { + migration_ioc_unregister_yank_from_file(mis->postcopy_qemufile_dst); + qemu_fclose(mis->postcopy_qemufile_dst); + mis->postcopy_qemufile_dst = NULL; + } + yank_unregister_instance(MIGRATION_YANK_INSTANCE); } @@ -691,9 +700,9 @@ static bool postcopy_try_recover(void) /* * Here, we only wake up the main loading thread (while the - * fault thread will still be waiting), so that we can receive + * rest threads will still be waiting), so that we can receive * commands from source now, and answer it if needed. The - * fault thread will be woken up afterwards until we are sure + * rest threads will be woken up afterwards until we are sure * that source is ready to reply to page requests. */ qemu_sem_post(&mis->postcopy_pause_sem_dst); @@ -714,15 +723,21 @@ void migration_fd_process_incoming(QEMUFile *f, Error **errp) migration_incoming_process(); } +static bool migration_needs_multiple_sockets(void) +{ + return migrate_use_multifd() || migrate_postcopy_preempt(); +} + void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; bool start_migration; + QEMUFile *f; if (!mis->from_src_file) { /* The first connection (multifd may have multiple) */ - QEMUFile *f = qemu_fopen_channel_input(ioc); + f = qemu_file_new_input(ioc); if (!migration_incoming_setup(f, errp)) { return; @@ -730,13 +745,19 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) /* * Common migration only needs one channel, so we can start - * right now. Multifd needs more than one channel, we wait. + * right now. Some features need more than one channel, we wait. */ - start_migration = !migrate_use_multifd(); + start_migration = !migration_needs_multiple_sockets(); } else { /* Multiple connections */ - assert(migrate_use_multifd()); - start_migration = multifd_recv_new_channel(ioc, &local_err); + assert(migration_needs_multiple_sockets()); + if (migrate_use_multifd()) { + start_migration = multifd_recv_new_channel(ioc, &local_err); + } else { + assert(migrate_postcopy_preempt()); + f = qemu_file_new_input(ioc); + start_migration = postcopy_preempt_new_channel(mis, f); + } if (local_err) { error_propagate(errp, local_err); return; @@ -761,11 +782,20 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) bool migration_has_all_channels(void) { MigrationIncomingState *mis = migration_incoming_get_current(); - bool all_channels; - all_channels = multifd_recv_all_channels_created(); + if (!mis->from_src_file) { + return false; + } - return all_channels && mis->from_src_file != NULL; + if (migrate_use_multifd()) { + return multifd_recv_all_channels_created(); + } + + if (migrate_postcopy_preempt()) { + return mis->postcopy_qemufile_dst != NULL; + } + + return true; } /* @@ -910,10 +940,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->multifd_zlib_level = s->parameters.multifd_zlib_level; params->has_multifd_zstd_level = true; params->multifd_zstd_level = s->parameters.multifd_zstd_level; -#ifdef CONFIG_LINUX - params->has_zero_copy_send = true; - params->zero_copy_send = s->parameters.zero_copy_send; -#endif params->has_xbzrle_cache_size = true; params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; params->has_max_postcopy_bandwidth = true; @@ -1031,6 +1057,8 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->normal_bytes = ram_counters.normal * page_size; info->ram->mbps = s->mbps; info->ram->dirty_sync_count = ram_counters.dirty_sync_count; + info->ram->dirty_sync_missed_zero_copy = + ram_counters.dirty_sync_missed_zero_copy; info->ram->postcopy_requests = ram_counters.postcopy_requests; info->ram->page_size = page_size; info->ram->multifd_bytes = ram_counters.multifd_bytes; @@ -1275,6 +1303,26 @@ static bool migrate_caps_check(bool *cap_list, } } +#ifdef CONFIG_LINUX + if (cap_list[MIGRATION_CAPABILITY_ZERO_COPY_SEND] && + (!cap_list[MIGRATION_CAPABILITY_MULTIFD] || + cap_list[MIGRATION_CAPABILITY_COMPRESS] || + cap_list[MIGRATION_CAPABILITY_XBZRLE] || + migrate_multifd_compression() || + migrate_use_tls())) { + error_setg(errp, + "Zero copy only available for non-compressed non-TLS multifd migration"); + return false; + } +#else + if (cap_list[MIGRATION_CAPABILITY_ZERO_COPY_SEND]) { + error_setg(errp, + "Zero copy currently only available on Linux"); + return false; + } +#endif + + /* incoming side only */ if (runstate_check(RUN_STATE_INMIGRATE) && !migrate_multi_channels_is_allowed() && @@ -1283,6 +1331,13 @@ static bool migrate_caps_check(bool *cap_list, return false; } + if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) { + if (!cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + error_setg(errp, "Postcopy preempt requires postcopy-ram"); + return false; + } + } + return true; } @@ -1497,16 +1552,17 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: "); return false; } + #ifdef CONFIG_LINUX - if (params->zero_copy_send && - (!migrate_use_multifd() || - params->multifd_compression != MULTIFD_COMPRESSION_NONE || - (params->tls_creds && *params->tls_creds))) { + if (migrate_use_zero_copy_send() && + ((params->has_multifd_compression && params->multifd_compression) || + (params->has_tls_creds && params->tls_creds && *params->tls_creds))) { error_setg(errp, "Zero copy only available for non-compressed non-TLS multifd migration"); return false; } #endif + return true; } @@ -1580,11 +1636,6 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_multifd_compression) { dest->multifd_compression = params->multifd_compression; } -#ifdef CONFIG_LINUX - if (params->has_zero_copy_send) { - dest->zero_copy_send = params->zero_copy_send; - } -#endif if (params->has_xbzrle_cache_size) { dest->xbzrle_cache_size = params->xbzrle_cache_size; } @@ -1697,11 +1748,6 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_multifd_compression) { s->parameters.multifd_compression = params->multifd_compression; } -#ifdef CONFIG_LINUX - if (params->has_zero_copy_send) { - s->parameters.zero_copy_send = params->zero_copy_send; - } -#endif if (params->has_xbzrle_cache_size) { s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; xbzrle_cache_resize(params->xbzrle_cache_size, errp); @@ -1873,6 +1919,12 @@ static void migrate_fd_cleanup(MigrationState *s) 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)); if (s->state == MIGRATION_STATUS_CANCELLING) { @@ -2593,7 +2645,7 @@ bool migrate_use_zero_copy_send(void) s = migrate_get_current(); - return s->parameters.zero_copy_send; + return s->enabled_capabilities[MIGRATION_CAPABILITY_ZERO_COPY_SEND]; } #endif @@ -2669,6 +2721,15 @@ bool migrate_background_snapshot(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; } +bool migrate_postcopy_preempt(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]; +} + /* migration thread support */ /* * Something bad happened to the RP stream, mark an error @@ -3008,6 +3069,12 @@ static int postcopy_start(MigrationState *ms) int64_t bandwidth = migrate_max_postcopy_bandwidth(); bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; + + if (postcopy_preempt_wait_channel(ms)) { + migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED); + return -1; + } + if (!migrate_pause_before_switchover()) { migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_POSTCOPY_ACTIVE); @@ -3081,7 +3148,7 @@ static int postcopy_start(MigrationState *ms) */ bioc = qio_channel_buffer_new(4096); qio_channel_set_name(QIO_CHANNEL(bioc), "migration-postcopy-buffer"); - fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc)); + fb = qemu_file_new_output(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); /* @@ -3147,6 +3214,8 @@ static int postcopy_start(MigrationState *ms) MIGRATION_STATUS_FAILED); } + trace_postcopy_preempt_enabled(migrate_postcopy_preempt()); + return ret; fail_closefb: @@ -3259,6 +3328,11 @@ static void migration_completion(MigrationState *s) qemu_savevm_state_complete_postcopy(s->to_dst_file); qemu_mutex_unlock_iothread(); + /* Shutdown the postcopy fast path thread */ + if (migrate_postcopy_preempt()) { + postcopy_preempt_shutdown_file(s); + } + trace_migration_completion_postcopy_end_after_complete(); } else { goto fail; @@ -3453,6 +3527,18 @@ static MigThrError postcopy_pause(MigrationState *s) qemu_file_shutdown(file); qemu_fclose(file); + /* + * Do the same to postcopy fast path socket too if there is. No + * locking needed because no racer as long as we do this before setting + * status to paused. + */ + if (s->postcopy_qemufile_src) { + migration_ioc_unregister_yank_from_file(s->postcopy_qemufile_src); + qemu_file_shutdown(s->postcopy_qemufile_src); + qemu_fclose(s->postcopy_qemufile_src); + s->postcopy_qemufile_src = NULL; + } + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_POSTCOPY_PAUSED); @@ -3470,6 +3556,14 @@ static MigThrError postcopy_pause(MigrationState *s) if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { /* Woken up by a recover procedure. Give it a shot */ + if (postcopy_preempt_wait_channel(s)) { + /* + * Preempt enabled, and new channel create failed; loop + * back to wait for another recovery. + */ + continue; + } + /* * Firstly, let's wake up the return path now, with a new * return path channel. @@ -3508,8 +3602,13 @@ static MigThrError migration_detect_error(MigrationState *s) return MIG_THR_ERR_FATAL; } - /* Try to detect any file errors */ - ret = qemu_file_get_error_obj(s->to_dst_file, &local_error); + /* + * Try to detect any file errors. Note that postcopy_qemufile_src will + * be NULL when postcopy preempt is not enabled. + */ + ret = qemu_file_get_error_obj_any(s->to_dst_file, + s->postcopy_qemufile_src, + &local_error); if (!ret) { /* Everything is fine */ assert(!local_error); @@ -3544,7 +3643,8 @@ static MigThrError migration_detect_error(MigrationState *s) /* How many bytes have we transferred since the beginning of the migration */ static uint64_t migration_total_bytes(MigrationState *s) { - return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes; + return qemu_file_total_transferred(s->to_dst_file) + + ram_counters.multifd_bytes; } static void migration_calculate_complete(MigrationState *s) @@ -3970,7 +4070,7 @@ static void *bg_migration_thread(void *opaque) */ s->bioc = qio_channel_buffer_new(512 * 1024); qio_channel_set_name(QIO_CHANNEL(s->bioc), "vmstate-buffer"); - fb = qemu_fopen_channel_output(QIO_CHANNEL(s->bioc)); + fb = qemu_file_new_output(QIO_CHANNEL(s->bioc)); object_unref(OBJECT(s->bioc)); update_iteration_initial_status(s); @@ -4146,6 +4246,15 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) } } + /* This needs to be done before resuming a postcopy */ + if (postcopy_preempt_setup(s, &local_err)) { + error_report_err(local_err); + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); + migrate_fd_cleanup(s); + return; + } + if (resume) { /* Wakeup the main migration thread to do the recovery */ migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, @@ -4249,10 +4358,6 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("multifd-zstd-level", MigrationState, parameters.multifd_zstd_level, DEFAULT_MIGRATE_MULTIFD_ZSTD_LEVEL), -#ifdef CONFIG_LINUX - DEFINE_PROP_BOOL("zero_copy_send", MigrationState, - parameters.zero_copy_send, false), -#endif DEFINE_PROP_SIZE("xbzrle-cache-size", MigrationState, parameters.xbzrle_cache_size, DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE), @@ -4274,6 +4379,11 @@ static Property migration_properties[] = { DEFINE_PROP_SIZE("announce-step", MigrationState, parameters.announce_step, DEFAULT_MIGRATE_ANNOUNCE_STEP), + DEFINE_PROP_BOOL("x-postcopy-preempt-break-huge", MigrationState, + postcopy_preempt_break_huge, true), + DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds), + DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname), + DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -4283,6 +4393,8 @@ static Property migration_properties[] = { 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), @@ -4290,6 +4402,10 @@ static Property migration_properties[] = { DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD), DEFINE_PROP_MIG_CAP("x-background-snapshot", MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT), +#ifdef CONFIG_LINUX + DEFINE_PROP_MIG_CAP("x-zero-copy-send", + MIGRATION_CAPABILITY_ZERO_COPY_SEND), +#endif DEFINE_PROP_END_OF_LIST(), }; @@ -4305,18 +4421,16 @@ static void migration_class_init(ObjectClass *klass, void *data) static void migration_instance_finalize(Object *obj) { MigrationState *ms = MIGRATION_OBJ(obj); - MigrationParameters *params = &ms->parameters; qemu_mutex_destroy(&ms->error_mutex); qemu_mutex_destroy(&ms->qemu_file_lock); - g_free(params->tls_hostname); - g_free(params->tls_creds); qemu_sem_destroy(&ms->wait_unplug_sem); qemu_sem_destroy(&ms->rate_limit_sem); qemu_sem_destroy(&ms->pause_sem); qemu_sem_destroy(&ms->postcopy_pause_sem); qemu_sem_destroy(&ms->postcopy_pause_rp_sem); qemu_sem_destroy(&ms->rp_state.rp_sem); + qemu_sem_destroy(&ms->postcopy_qemufile_src_sem); error_free(ms->error); } @@ -4350,9 +4464,6 @@ static void migration_instance_init(Object *obj) params->has_multifd_compression = true; params->has_multifd_zlib_level = true; params->has_multifd_zstd_level = true; -#ifdef CONFIG_LINUX - params->has_zero_copy_send = true; -#endif params->has_xbzrle_cache_size = true; params->has_max_postcopy_bandwidth = true; params->has_max_cpu_throttle = true; @@ -4366,6 +4477,7 @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->rp_state.rp_sem, 0); qemu_sem_init(&ms->rate_limit_sem, 0); qemu_sem_init(&ms->wait_unplug_sem, 0); + qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0); qemu_mutex_init(&ms->qemu_file_lock); } diff --git a/migration/migration.h b/migration/migration.h index 485d58b95f8..cdad8aceaaa 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -23,6 +23,7 @@ #include "io/channel-buffer.h" #include "net/announce.h" #include "qom/object.h" +#include "postcopy-ram.h" struct PostcopyBlocktimeContext; @@ -67,7 +68,7 @@ typedef struct { struct MigrationIncomingState { QEMUFile *from_src_file; /* Previously received RAM's RAMBlock pointer */ - RAMBlock *last_recv_block; + RAMBlock *last_recv_block[RAM_CHANNEL_MAX]; /* A hook to allow cleanup at the end of incoming migration */ void *transport_data; void (*transport_cleanup)(void *data); @@ -112,6 +113,23 @@ struct MigrationIncomingState { * enabled. */ unsigned int postcopy_channels; + /* QEMUFile for postcopy only; it'll be handled by a separate thread */ + QEMUFile *postcopy_qemufile_dst; + /* Postcopy priority thread is used to receive postcopy requested pages */ + QemuThread postcopy_prio_thread; + bool postcopy_prio_thread_created; + /* + * Used to sync between the ram load main thread and the fast ram load + * thread. It protects postcopy_qemufile_dst, which is the postcopy + * fast channel. + * + * The ram fast load thread will take it mostly for the whole lifecycle + * because it needs to continuously read data from the channel, and + * it'll only release this mutex if postcopy is interrupted, so that + * the ram load main thread will take this mutex over and properly + * release the broken channel. + */ + QemuMutex postcopy_prio_thread_mutex; /* * An array of temp host huge pages to be used, one for each postcopy * channel. @@ -141,6 +159,13 @@ struct MigrationIncomingState { /* notify PAUSED postcopy incoming migrations to try to continue */ QemuSemaphore postcopy_pause_sem_dst; QemuSemaphore postcopy_pause_sem_fault; + /* + * This semaphore is used to allow the ram fast load thread (only when + * postcopy preempt is enabled) fall into sleep when there's network + * interruption detected. When the recovery is done, the main load + * thread will kick the fast ram load thread using this semaphore. + */ + QemuSemaphore postcopy_pause_sem_fast_load; /* List of listening socket addresses */ SocketAddressList *socket_address_list; @@ -192,6 +217,15 @@ struct MigrationState { QEMUBH *cleanup_bh; /* Protected by qemu_file_lock */ QEMUFile *to_dst_file; + /* Postcopy specific transfer channel */ + QEMUFile *postcopy_qemufile_src; + /* + * It is posted when the preempt channel is established. Note: this is + * used for both the start or recover of a postcopy migration. We'll + * post to this sem every time a new preempt channel is created in the + * main thread, and we keep post() and wait() in pair. + */ + QemuSemaphore postcopy_qemufile_src_sem; QIOChannelBuffer *bioc; /* * Protects to_dst_file/from_dst_file pointers. We need to make sure we @@ -306,6 +340,13 @@ struct MigrationState { bool send_configuration; /* Whether we send section footer during migration */ bool send_section_footer; + /* + * Whether we allow break sending huge pages when postcopy preempt is + * enabled. When disabled, we won't interrupt precopy within sending a + * host huge page, which is the old behavior of vanilla postcopy. + * NOTE: this parameter is ignored if postcopy preempt is not enabled. + */ + bool postcopy_preempt_break_huge; /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; @@ -400,6 +441,7 @@ int migrate_decompress_threads(void); bool migrate_use_events(void); bool migrate_postcopy_blocktime(void); bool migrate_background_snapshot(void); +bool migrate_postcopy_preempt(void); /* Sending on the return path - generic and then for each message type */ void migrate_send_rp_shut(MigrationIncomingState *mis, diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 3a7ae44485d..18213a95130 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -27,6 +27,8 @@ struct zlib_data { uint8_t *zbuff; /* size of compressed buffer */ uint32_t zbuff_len; + /* uncompressed buffer of size qemu_target_page_size() */ + uint8_t *buf; }; /* Multifd zlib compression */ @@ -45,26 +47,38 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) { struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; + const char *err_msg; zs->zalloc = Z_NULL; zs->zfree = Z_NULL; zs->opaque = Z_NULL; if (deflateInit(zs, migrate_multifd_zlib_level()) != Z_OK) { - g_free(z); - error_setg(errp, "multifd %u: deflate init failed", p->id); - return -1; + err_msg = "deflate init failed"; + goto err_free_z; } /* This is the maxium size of the compressed buffer */ z->zbuff_len = compressBound(MULTIFD_PACKET_SIZE); z->zbuff = g_try_malloc(z->zbuff_len); if (!z->zbuff) { - deflateEnd(&z->zs); - g_free(z); - error_setg(errp, "multifd %u: out of memory for zbuff", p->id); - return -1; + err_msg = "out of memory for zbuff"; + goto err_deflate_end; + } + z->buf = g_try_malloc(qemu_target_page_size()); + if (!z->buf) { + err_msg = "out of memory for buf"; + goto err_free_zbuff; } p->data = z; return 0; + +err_free_zbuff: + g_free(z->zbuff); +err_deflate_end: + deflateEnd(&z->zs); +err_free_z: + g_free(z); + error_setg(errp, "multifd %u: %s", p->id, err_msg); + return -1; } /** @@ -82,6 +96,8 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) deflateEnd(&z->zs); g_free(z->zbuff); z->zbuff = NULL; + g_free(z->buf); + z->buf = NULL; g_free(p->data); p->data = NULL; } @@ -114,8 +130,14 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) flush = Z_SYNC_FLUSH; } + /* + * Since the VM might be running, the page may be changing concurrently + * with compression. zlib does not guarantee that this is safe, + * therefore copy the page before calling deflate(). + */ + memcpy(z->buf, p->pages->block->host + p->normal[i], page_size); zs->avail_in = page_size; - zs->next_in = p->pages->block->host + p->normal[i]; + zs->next_in = z->buf; zs->avail_out = available; zs->next_out = z->zbuff + out_size; diff --git a/migration/multifd.c b/migration/multifd.c index 9282ab6aa41..586ddc9d657 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -435,7 +435,7 @@ static int multifd_send_pages(QEMUFile *f) p->pages = pages; transferred = ((uint64_t) pages->num) * qemu_target_page_size() + p->packet_len; - qemu_file_update_transfer(f, transferred); + qemu_file_acct_rate_limit(f, transferred); ram_counters.multifd_bytes += transferred; ram_counters.transferred += transferred; qemu_mutex_unlock(&p->mutex); @@ -610,7 +610,7 @@ int multifd_send_sync_main(QEMUFile *f) p->packet_num = multifd_send_state->packet_num++; p->flags |= MULTIFD_FLAG_SYNC; p->pending_job++; - qemu_file_update_transfer(f, p->packet_len); + qemu_file_acct_rate_limit(f, p->packet_len); ram_counters.multifd_bytes += p->packet_len; ram_counters.transferred += p->packet_len; qemu_mutex_unlock(&p->mutex); @@ -624,6 +624,8 @@ int multifd_send_sync_main(QEMUFile *f) if (ret < 0) { error_report_err(err); return -1; + } else if (ret == 1) { + dirty_sync_missed_zero_copy(); } } } @@ -831,9 +833,7 @@ static bool multifd_channel_connect(MultiFDSendParams *p, migrate_get_current()->hostname, error); if (!error) { - if (migrate_use_tls() && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { + if (migrate_channel_requires_tls_upgrade(ioc)) { multifd_tls_channel_connect(p, ioc, &error); if (!error) { /* diff --git a/migration/multifd.h b/migration/multifd.h index 4d8d89e5e58..519f498643b 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -65,7 +65,9 @@ typedef struct { } MultiFDPages_t; typedef struct { - /* this fields are not changed once the thread is created */ + /* Fields are only written at creating/deletion time */ + /* No lock required for them, they are read only */ + /* channel number */ uint8_t id; /* channel thread name */ @@ -74,39 +76,47 @@ typedef struct { QemuThread thread; /* communication channel */ QIOChannel *c; + /* is the yank function registered */ + bool registered_yank; + /* packet allocated len */ + uint32_t packet_len; + /* multifd flags for sending ram */ + int write_flags; + /* sem where to wait for more work */ QemuSemaphore sem; + /* 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; - /* is the yank function registered */ - bool registered_yank; + /* 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 */ + /* array of pages to sent. + * The owner of 'pages' depends of 'pending_job' value: + * pending_job == 0 -> migration_thread can use it. + * pending_job != 0 -> multifd_channel can use it. + */ MultiFDPages_t *pages; - /* packet allocated len */ - uint32_t packet_len; + + /* thread local variables. No locking required */ + /* pointer to the packet */ MultiFDPacket_t *packet; - /* multifd flags for sending ram */ - int write_flags; - /* multifd flags for each packet */ - uint32_t flags; /* size of the next packet that contains pages */ uint32_t next_packet_size; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* thread local variables */ /* packets sent through this channel */ uint64_t num_packets; /* non zero pages sent through this channel */ uint64_t total_normal_pages; - /* syncs main thread and channels */ - QemuSemaphore sem_sync; /* buffers to send */ struct iovec *iov; /* number of iovs used */ @@ -120,7 +130,9 @@ typedef struct { } MultiFDSendParams; typedef struct { - /* this fields are not changed once the thread is created */ + /* Fields are only written at creating/deletion time */ + /* No lock required for them, they are read only */ + /* channel number */ uint8_t id; /* channel thread name */ @@ -129,31 +141,35 @@ typedef struct { QemuThread thread; /* communication channel */ QIOChannel *c; + /* packet allocated len */ + uint32_t packet_len; + + /* 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; - /* ramblock host address */ - uint8_t *host; - /* packet allocated len */ - uint32_t packet_len; - /* pointer to the packet */ - MultiFDPacket_t *packet; /* multifd flags for each packet */ uint32_t flags; /* global number of generated multifd packets */ uint64_t packet_num; - /* thread local variables */ + + /* thread local variables. No locking required */ + + /* pointer to the packet */ + MultiFDPacket_t *packet; /* size of the next packet that contains pages */ uint32_t next_packet_size; /* packets sent through this channel */ uint64_t num_packets; + /* ramblock host address */ + uint8_t *host; /* non zero pages recv through this channel */ uint64_t total_normal_pages; - /* syncs main thread and channels */ - QemuSemaphore sem_sync; /* buffers to recv */ struct iovec *iov; /* Pages that are not zero */ diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index a66dd536d93..b9a37ef2558 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -33,6 +33,10 @@ #include "trace.h" #include "hw/boards.h" #include "exec/ramblock.h" +#include "socket.h" +#include "qemu-file.h" +#include "yank_functions.h" +#include "tls.h" /* Arbitrary limit on size of each discard command, * keeps them around ~200 bytes @@ -567,6 +571,11 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) { trace_postcopy_ram_incoming_cleanup_entry(); + if (mis->postcopy_prio_thread_created) { + qemu_thread_join(&mis->postcopy_prio_thread); + mis->postcopy_prio_thread_created = false; + } + if (mis->have_fault_thread) { Error *local_err = NULL; @@ -1102,8 +1111,13 @@ static int postcopy_temp_pages_setup(MigrationIncomingState *mis) int err, i, channels; void *temp_page; - /* TODO: will be boosted when enable postcopy preemption */ - mis->postcopy_channels = 1; + if (migrate_postcopy_preempt()) { + /* If preemption enabled, need extra channel for urgent requests */ + mis->postcopy_channels = RAM_CHANNEL_MAX; + } else { + /* Both precopy/postcopy on the same channel */ + mis->postcopy_channels = 1; + } channels = mis->postcopy_channels; mis->postcopy_tmp_pages = g_malloc0_n(sizeof(PostcopyTmpPage), channels); @@ -1170,7 +1184,7 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } - postcopy_thread_create(mis, &mis->fault_thread, "postcopy/fault", + postcopy_thread_create(mis, &mis->fault_thread, "fault-default", postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE); mis->have_fault_thread = true; @@ -1185,6 +1199,16 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } + if (migrate_postcopy_preempt()) { + /* + * This thread needs to be created after the temp pages because + * it'll fetch RAM_CHANNEL_POSTCOPY PostcopyTmpPage immediately. + */ + postcopy_thread_create(mis, &mis->postcopy_prio_thread, "fault-fast", + postcopy_preempt_thread, QEMU_THREAD_JOINABLE); + mis->postcopy_prio_thread_created = true; + } + trace_postcopy_ram_enable_notify(); return 0; @@ -1514,3 +1538,159 @@ void postcopy_unregister_shared_ufd(struct PostCopyFD *pcfd) } } } + +bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) +{ + /* + * The new loading channel has its own threads, so it needs to be + * blocked too. It's by default true, just be explicit. + */ + qemu_file_set_blocking(file, true); + mis->postcopy_qemufile_dst = file; + trace_postcopy_preempt_new_channel(); + + /* Start the migration immediately */ + return true; +} + +/* + * Setup the postcopy preempt channel with the IOC. If ERROR is specified, + * setup the error instead. This helper will free the ERROR if specified. + */ +static void +postcopy_preempt_send_channel_done(MigrationState *s, + QIOChannel *ioc, Error *local_err) +{ + if (local_err) { + migrate_set_error(s, local_err); + error_free(local_err); + } else { + migration_ioc_register_yank(ioc); + s->postcopy_qemufile_src = qemu_file_new_output(ioc); + trace_postcopy_preempt_new_channel(); + } + + /* + * Kick the waiter in all cases. The waiter should check upon + * postcopy_qemufile_src to know whether it failed or not. + */ + qemu_sem_post(&s->postcopy_qemufile_src_sem); +} + +static void +postcopy_preempt_tls_handshake(QIOTask *task, gpointer opaque) +{ + g_autoptr(QIOChannel) ioc = QIO_CHANNEL(qio_task_get_source(task)); + MigrationState *s = opaque; + Error *local_err = NULL; + + qio_task_propagate_error(task, &local_err); + postcopy_preempt_send_channel_done(s, ioc, local_err); +} + +static void +postcopy_preempt_send_channel_new(QIOTask *task, gpointer opaque) +{ + g_autoptr(QIOChannel) ioc = QIO_CHANNEL(qio_task_get_source(task)); + MigrationState *s = opaque; + QIOChannelTLS *tioc; + Error *local_err = NULL; + + if (qio_task_propagate_error(task, &local_err)) { + goto out; + } + + if (migrate_channel_requires_tls_upgrade(ioc)) { + tioc = migration_tls_client_create(s, ioc, s->hostname, &local_err); + if (!tioc) { + goto out; + } + trace_postcopy_preempt_tls_handshake(); + qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-preempt"); + qio_channel_tls_handshake(tioc, postcopy_preempt_tls_handshake, + s, NULL, NULL); + /* Setup the channel until TLS handshake finished */ + return; + } + +out: + /* This handles both good and error cases */ + postcopy_preempt_send_channel_done(s, ioc, local_err); +} + +/* Returns 0 if channel established, -1 for error. */ +int postcopy_preempt_wait_channel(MigrationState *s) +{ + /* If preempt not enabled, no need to wait */ + if (!migrate_postcopy_preempt()) { + return 0; + } + + /* + * We need the postcopy preempt channel to be established before + * starting doing anything. + */ + qemu_sem_wait(&s->postcopy_qemufile_src_sem); + + return s->postcopy_qemufile_src ? 0 : -1; +} + +int postcopy_preempt_setup(MigrationState *s, Error **errp) +{ + if (!migrate_postcopy_preempt()) { + return 0; + } + + if (!migrate_multi_channels_is_allowed()) { + error_setg(errp, "Postcopy preempt is not supported as current " + "migration stream does not support multi-channels."); + return -1; + } + + /* Kick an async task to connect */ + socket_send_channel_create(postcopy_preempt_send_channel_new, s); + + return 0; +} + +static void postcopy_pause_ram_fast_load(MigrationIncomingState *mis) +{ + trace_postcopy_pause_fast_load(); + qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); + qemu_sem_wait(&mis->postcopy_pause_sem_fast_load); + qemu_mutex_lock(&mis->postcopy_prio_thread_mutex); + trace_postcopy_pause_fast_load_continued(); +} + +void *postcopy_preempt_thread(void *opaque) +{ + MigrationIncomingState *mis = opaque; + int ret; + + trace_postcopy_preempt_thread_entry(); + + rcu_register_thread(); + + qemu_sem_post(&mis->thread_sync_sem); + + /* Sending RAM_SAVE_FLAG_EOS to terminate this thread */ + qemu_mutex_lock(&mis->postcopy_prio_thread_mutex); + while (1) { + ret = ram_load_postcopy(mis->postcopy_qemufile_dst, + RAM_CHANNEL_POSTCOPY); + /* If error happened, go into recovery routine */ + if (ret) { + postcopy_pause_ram_fast_load(mis); + } else { + /* We're done */ + break; + } + } + qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); + + rcu_unregister_thread(); + + trace_postcopy_preempt_thread_exit(); + + return NULL; +} diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index 07684c0e1dd..6147bf7d1d0 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -183,4 +183,15 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, uint64_t client_addr, int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, uint64_t client_addr, uint64_t offset); +/* Hard-code channels for now for postcopy preemption */ +enum PostcopyChannels { + RAM_CHANNEL_PRECOPY = 0, + RAM_CHANNEL_POSTCOPY = 1, + RAM_CHANNEL_MAX, +}; + +bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file); +int postcopy_preempt_setup(MigrationState *s, Error **errp); +int postcopy_preempt_wait_channel(MigrationState *s); + #endif diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c deleted file mode 100644 index bb5a5752df2..00000000000 --- a/migration/qemu-file-channel.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * QEMUFile backend for QIOChannel objects - * - * Copyright (c) 2015-2016 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-file-channel.h" -#include "qemu-file.h" -#include "io/channel-socket.h" -#include "io/channel-tls.h" -#include "qemu/iov.h" -#include "qemu/yank.h" -#include "yank_functions.h" - - -static ssize_t channel_writev_buffer(void *opaque, - struct iovec *iov, - int iovcnt, - int64_t pos, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - ssize_t done = 0; - struct iovec *local_iov = g_new(struct iovec, iovcnt); - struct iovec *local_iov_head = local_iov; - unsigned int nlocal_iov = iovcnt; - - nlocal_iov = iov_copy(local_iov, nlocal_iov, - iov, iovcnt, - 0, iov_size(iov, iovcnt)); - - while (nlocal_iov > 0) { - ssize_t len; - len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); - if (len == QIO_CHANNEL_ERR_BLOCK) { - if (qemu_in_coroutine()) { - qio_channel_yield(ioc, G_IO_OUT); - } else { - qio_channel_wait(ioc, G_IO_OUT); - } - continue; - } - if (len < 0) { - done = -EIO; - goto cleanup; - } - - iov_discard_front(&local_iov, &nlocal_iov, len); - done += len; - } - - cleanup: - g_free(local_iov_head); - return done; -} - - -static ssize_t channel_get_buffer(void *opaque, - uint8_t *buf, - int64_t pos, - size_t size, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - ssize_t ret; - - do { - ret = qio_channel_read(ioc, (char *)buf, size, errp); - if (ret < 0) { - if (ret == QIO_CHANNEL_ERR_BLOCK) { - if (qemu_in_coroutine()) { - qio_channel_yield(ioc, G_IO_IN); - } else { - qio_channel_wait(ioc, G_IO_IN); - } - } else { - return -EIO; - } - } - } while (ret == QIO_CHANNEL_ERR_BLOCK); - - return ret; -} - - -static int channel_close(void *opaque, Error **errp) -{ - int ret; - QIOChannel *ioc = QIO_CHANNEL(opaque); - ret = qio_channel_close(ioc, errp); - object_unref(OBJECT(ioc)); - return ret; -} - - -static int channel_shutdown(void *opaque, - bool rd, - bool wr, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - if (qio_channel_has_feature(ioc, - QIO_CHANNEL_FEATURE_SHUTDOWN)) { - QIOChannelShutdown mode; - if (rd && wr) { - mode = QIO_CHANNEL_SHUTDOWN_BOTH; - } else if (rd) { - mode = QIO_CHANNEL_SHUTDOWN_READ; - } else { - mode = QIO_CHANNEL_SHUTDOWN_WRITE; - } - if (qio_channel_shutdown(ioc, mode, errp) < 0) { - return -EIO; - } - } - return 0; -} - - -static int channel_set_blocking(void *opaque, - bool enabled, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - if (qio_channel_set_blocking(ioc, enabled, errp) < 0) { - return -1; - } - return 0; -} - -static QEMUFile *channel_get_input_return_path(void *opaque) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - return qemu_fopen_channel_output(ioc); -} - -static QEMUFile *channel_get_output_return_path(void *opaque) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - return qemu_fopen_channel_input(ioc); -} - -static const QEMUFileOps channel_input_ops = { - .get_buffer = channel_get_buffer, - .close = channel_close, - .shut_down = channel_shutdown, - .set_blocking = channel_set_blocking, - .get_return_path = channel_get_input_return_path, -}; - - -static const QEMUFileOps channel_output_ops = { - .writev_buffer = channel_writev_buffer, - .close = channel_close, - .shut_down = channel_shutdown, - .set_blocking = channel_set_blocking, - .get_return_path = channel_get_output_return_path, -}; - - -QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc) -{ - object_ref(OBJECT(ioc)); - return qemu_fopen_ops(ioc, &channel_input_ops, true); -} - -QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc) -{ - object_ref(OBJECT(ioc)); - return qemu_fopen_ops(ioc, &channel_output_ops, true); -} diff --git a/migration/qemu-file-channel.h b/migration/qemu-file-channel.h deleted file mode 100644 index 0028a09eb61..00000000000 --- a/migration/qemu-file-channel.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * QEMUFile backend for QIOChannel objects - * - * Copyright (c) 2015-2016 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_FILE_CHANNEL_H -#define QEMU_FILE_CHANNEL_H - -#include "io/channel.h" - -QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc); -QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc); -#endif diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 1479cddad94..4f400c2e526 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -35,15 +35,24 @@ #define MAX_IOV_SIZE MIN_CONST(IOV_MAX, 64) struct QEMUFile { - const QEMUFileOps *ops; const QEMUFileHooks *hooks; - void *opaque; + QIOChannel *ioc; + bool is_writable; - int64_t bytes_xfer; - int64_t xfer_limit; + /* + * Maximum amount of data in bytes to transfer during one + * rate limiting time window + */ + int64_t rate_limit_max; + /* + * Total amount of data in bytes queued for transfer + * during this rate limiting time window + */ + int64_t rate_limit_used; + + /* The sum of bytes transferred on the wire */ + int64_t total_transferred; - int64_t pos; /* start of buffer when writing, end of buffer - when reading */ int buf_index; int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; @@ -56,23 +65,28 @@ struct QEMUFile { Error *last_error_obj; /* has the file has been shutdown */ bool shutdown; - /* Whether opaque points to a QIOChannel */ - bool has_ioc; }; /* * Stop a file from being read/written - not all backing files can do this * typically only sockets can. + * + * TODO: convert to propagate Error objects instead of squashing + * to a fixed errno value */ int qemu_file_shutdown(QEMUFile *f) { - int ret; + int ret = 0; f->shutdown = true; - if (!f->ops->shut_down) { + if (!qio_channel_has_feature(f->ioc, + QIO_CHANNEL_FEATURE_SHUTDOWN)) { return -ENOSYS; } - ret = f->ops->shut_down(f->opaque, true, true, NULL); + + if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL) < 0) { + ret = -EIO; + } if (!f->last_error) { qemu_file_set_error(f, -EIO); @@ -80,18 +94,6 @@ int qemu_file_shutdown(QEMUFile *f) return ret; } -/* - * Result: QEMUFile* for a 'return path' for comms in the opposite direction - * NULL if not available - */ -QEMUFile *qemu_file_get_return_path(QEMUFile *f) -{ - if (!f->ops->get_return_path) { - return NULL; - } - return f->ops->get_return_path(f->opaque); -} - bool qemu_file_mode_is_not_valid(const char *mode) { if (mode == NULL || @@ -104,18 +106,37 @@ bool qemu_file_mode_is_not_valid(const char *mode) return false; } -QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops, bool has_ioc) +static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable) { QEMUFile *f; f = g_new0(QEMUFile, 1); - f->opaque = opaque; - f->ops = ops; - f->has_ioc = has_ioc; + object_ref(ioc); + f->ioc = ioc; + f->is_writable = is_writable; + return f; } +/* + * Result: QEMUFile* for a 'return path' for comms in the opposite direction + * NULL if not available + */ +QEMUFile *qemu_file_get_return_path(QEMUFile *f) +{ + return qemu_file_new_impl(f->ioc, !f->is_writable); +} + +QEMUFile *qemu_file_new_output(QIOChannel *ioc) +{ + return qemu_file_new_impl(ioc, true); +} + +QEMUFile *qemu_file_new_input(QIOChannel *ioc) +{ + return qemu_file_new_impl(ioc, false); +} void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks) { @@ -139,6 +160,33 @@ int qemu_file_get_error_obj(QEMUFile *f, Error **errp) return f->last_error; } +/* + * Get last error for either stream f1 or f2 with optional Error*. + * The error returned (non-zero) can be either from f1 or f2. + * + * If any of the qemufile* is NULL, then skip the check on that file. + * + * When there is no error on both qemufile, zero is returned. + */ +int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp) +{ + int ret = 0; + + if (f1) { + ret = qemu_file_get_error_obj(f1, errp); + /* If there's already error detected, return */ + if (ret) { + return ret; + } + } + + if (f2) { + ret = qemu_file_get_error_obj(f2, errp); + } + + return ret; +} + /* * Set the last error for stream f with optional Error* */ @@ -174,7 +222,7 @@ void qemu_file_set_error(QEMUFile *f, int ret) bool qemu_file_is_writable(QEMUFile *f) { - return f->ops->writev_buffer; + return f->is_writable; } static void qemu_iovec_release_ram(QEMUFile *f) @@ -212,6 +260,7 @@ static void qemu_iovec_release_ram(QEMUFile *f) memset(f->may_free, 0, sizeof(f->may_free)); } + /** * Flushes QEMUFile buffer * @@ -220,10 +269,6 @@ static void qemu_iovec_release_ram(QEMUFile *f) */ void qemu_fflush(QEMUFile *f) { - ssize_t ret = 0; - ssize_t expect = 0; - Error *local_error = NULL; - if (!qemu_file_is_writable(f)) { return; } @@ -232,22 +277,18 @@ void qemu_fflush(QEMUFile *f) return; } if (f->iovcnt > 0) { - expect = iov_size(f->iov, f->iovcnt); - ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos, - &local_error); + Error *local_error = NULL; + if (qio_channel_writev_all(f->ioc, + f->iov, f->iovcnt, + &local_error) < 0) { + qemu_file_set_error_obj(f, -EIO, local_error); + } else { + f->total_transferred += iov_size(f->iov, f->iovcnt); + } qemu_iovec_release_ram(f); } - if (ret >= 0) { - f->pos += ret; - } - /* We expect the QEMUFile write impl to send the full - * data set we requested, so sanity check that. - */ - if (ret != expect) { - qemu_file_set_error_obj(f, ret < 0 ? ret : -EIO, local_error); - } f->buf_index = 0; f->iovcnt = 0; } @@ -257,7 +298,7 @@ void ram_control_before_iterate(QEMUFile *f, uint64_t flags) int ret = 0; if (f->hooks && f->hooks->before_ram_iterate) { - ret = f->hooks->before_ram_iterate(f, f->opaque, flags, NULL); + ret = f->hooks->before_ram_iterate(f, flags, NULL); if (ret < 0) { qemu_file_set_error(f, ret); } @@ -269,7 +310,7 @@ void ram_control_after_iterate(QEMUFile *f, uint64_t flags) int ret = 0; if (f->hooks && f->hooks->after_ram_iterate) { - ret = f->hooks->after_ram_iterate(f, f->opaque, flags, NULL); + ret = f->hooks->after_ram_iterate(f, flags, NULL); if (ret < 0) { qemu_file_set_error(f, ret); } @@ -281,7 +322,7 @@ void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data) int ret = -EINVAL; if (f->hooks && f->hooks->hook_ram_load) { - ret = f->hooks->hook_ram_load(f, f->opaque, flags, data); + ret = f->hooks->hook_ram_load(f, flags, data); if (ret < 0) { qemu_file_set_error(f, ret); } @@ -301,16 +342,16 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, uint64_t *bytes_sent) { if (f->hooks && f->hooks->save_page) { - int ret = f->hooks->save_page(f, f->opaque, block_offset, + int ret = f->hooks->save_page(f, block_offset, offset, size, bytes_sent); if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - f->bytes_xfer += size; + f->rate_limit_used += size; } if (ret != RAM_SAVE_CONTROL_DELAYED && ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (bytes_sent && *bytes_sent > 0) { - qemu_update_position(f, *bytes_sent); + qemu_file_credit_transfer(f, *bytes_sent); } else if (ret < 0) { qemu_file_set_error(f, ret); } @@ -349,25 +390,37 @@ static ssize_t qemu_fill_buffer(QEMUFile *f) return 0; } - len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos, - IO_BUF_SIZE - pending, &local_error); + do { + len = qio_channel_read(f->ioc, + (char *)f->buf + pending, + IO_BUF_SIZE - pending, + &local_error); + if (len == QIO_CHANNEL_ERR_BLOCK) { + if (qemu_in_coroutine()) { + qio_channel_yield(f->ioc, G_IO_IN); + } else { + qio_channel_wait(f->ioc, G_IO_IN); + } + } else if (len < 0) { + len = -EIO; + } + } while (len == QIO_CHANNEL_ERR_BLOCK); + if (len > 0) { f->buf_size += len; - f->pos += len; + f->total_transferred += len; } else if (len == 0) { qemu_file_set_error_obj(f, -EIO, local_error); - } else if (len != -EAGAIN) { - qemu_file_set_error_obj(f, len, local_error); } else { - error_free(local_error); + qemu_file_set_error_obj(f, len, local_error); } return len; } -void qemu_update_position(QEMUFile *f, size_t size) +void qemu_file_credit_transfer(QEMUFile *f, size_t size) { - f->pos += size; + f->total_transferred += size; } /** Closes the file @@ -380,16 +433,16 @@ void qemu_update_position(QEMUFile *f, size_t size) */ int qemu_fclose(QEMUFile *f) { - int ret; + int ret, ret2; qemu_fflush(f); ret = qemu_file_get_error(f); - if (f->ops->close) { - int ret2 = f->ops->close(f->opaque, NULL); - if (ret >= 0) { - ret = ret2; - } + 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. */ @@ -457,7 +510,7 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, return; } - f->bytes_xfer += size; + f->rate_limit_used += size; add_to_iovec(f, buf, size, may_free); } @@ -475,7 +528,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size) l = size; } memcpy(f->buf + f->buf_index, buf, l); - f->bytes_xfer += l; + f->rate_limit_used += l; add_buf_to_iovec(f, l); if (qemu_file_get_error(f)) { break; @@ -492,7 +545,7 @@ void qemu_put_byte(QEMUFile *f, int v) } f->buf[f->buf_index] = v; - f->bytes_xfer++; + f->rate_limit_used++; add_buf_to_iovec(f, 1); } @@ -648,9 +701,9 @@ int qemu_get_byte(QEMUFile *f) return result; } -int64_t qemu_ftell_fast(QEMUFile *f) +int64_t qemu_file_total_transferred_fast(QEMUFile *f) { - int64_t ret = f->pos; + int64_t ret = f->total_transferred; int i; for (i = 0; i < f->iovcnt; i++) { @@ -660,10 +713,10 @@ int64_t qemu_ftell_fast(QEMUFile *f) return ret; } -int64_t qemu_ftell(QEMUFile *f) +int64_t qemu_file_total_transferred(QEMUFile *f) { qemu_fflush(f); - return f->pos; + return f->total_transferred; } int qemu_file_rate_limit(QEMUFile *f) @@ -674,7 +727,7 @@ int qemu_file_rate_limit(QEMUFile *f) if (qemu_file_get_error(f)) { return 1; } - if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) { + if (f->rate_limit_max > 0 && f->rate_limit_used > f->rate_limit_max) { return 1; } return 0; @@ -682,22 +735,22 @@ int qemu_file_rate_limit(QEMUFile *f) int64_t qemu_file_get_rate_limit(QEMUFile *f) { - return f->xfer_limit; + return f->rate_limit_max; } void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) { - f->xfer_limit = limit; + f->rate_limit_max = limit; } void qemu_file_reset_rate_limit(QEMUFile *f) { - f->bytes_xfer = 0; + f->rate_limit_used = 0; } -void qemu_file_update_transfer(QEMUFile *f, int64_t len) +void qemu_file_acct_rate_limit(QEMUFile *f, int64_t len) { - f->bytes_xfer += len; + f->rate_limit_used += len; } void qemu_put_be16(QEMUFile *f, unsigned int v) @@ -851,19 +904,18 @@ void qemu_put_counted_string(QEMUFile *f, const char *str) */ void qemu_file_set_blocking(QEMUFile *f, bool block) { - if (f->ops->set_blocking) { - f->ops->set_blocking(f->opaque, block, NULL); - } + qio_channel_set_blocking(f->ioc, block, NULL); } /* - * Return the ioc object if it's a migration channel. Note: it can return NULL - * for callers passing in a non-migration qemufile. E.g. see qemu_fopen_bdrv() - * and its usage in e.g. load_snapshot(). So we need to check against NULL - * before using it. If without the check, migration_incoming_state_destroy() - * could fail for load_snapshot(). + * qemu_file_get_ioc: + * + * Get the ioc object for the file, without incrementing + * the reference count. + * + * Returns: the ioc object */ QIOChannel *qemu_file_get_ioc(QEMUFile *file) { - return file->has_ioc ? QIO_CHANNEL(file->opaque) : NULL; + return file->ioc; } diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 3f36d4dc8c4..fa13d04d787 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -29,47 +29,12 @@ #include "exec/cpu-common.h" #include "io/channel.h" -/* Read a chunk of data from a file at the given position. The pos argument - * can be ignored if the file is only be used for streaming. The number of - * bytes actually read should be returned. - */ -typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, - int64_t pos, size_t size, - Error **errp); - -/* Close a file - * - * Return negative error number on error, 0 or positive value on success. - * - * The meaning of return value on success depends on the specific back-end being - * used. - */ -typedef int (QEMUFileCloseFunc)(void *opaque, Error **errp); - -/* Called to return the OS file descriptor associated to the QEMUFile. - */ -typedef int (QEMUFileGetFD)(void *opaque); - -/* Called to change the blocking mode of the file - */ -typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled, Error **errp); - -/* - * This function writes an iovec to file. The handler must write all - * of the data or return a negative errno value. - */ -typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov, - int iovcnt, int64_t pos, - Error **errp); - /* * This function provides hooks around different * stages of RAM migration. - * 'opaque' is the backend specific data in QEMUFile * 'data' is call specific data associated with the 'flags' value */ -typedef int (QEMURamHookFunc)(QEMUFile *f, void *opaque, uint64_t flags, - void *data); +typedef int (QEMURamHookFunc)(QEMUFile *f, uint64_t flags, void *data); /* * Constants used by ram_control_* hooks @@ -84,34 +49,11 @@ typedef int (QEMURamHookFunc)(QEMUFile *f, void *opaque, uint64_t flags, * This function allows override of where the RAM page * is saved (such as RDMA, for example.) */ -typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque, - ram_addr_t block_offset, - ram_addr_t offset, - size_t size, - uint64_t *bytes_sent); - -/* - * Return a QEMUFile for comms in the opposite direction - */ -typedef QEMUFile *(QEMURetPathFunc)(void *opaque); - -/* - * Stop any read or write (depending on flags) on the underlying - * transport on the QEMUFile. - * Existing blocking reads/writes must be woken - * Returns 0 on success, -err on error - */ -typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr, - Error **errp); - -typedef struct QEMUFileOps { - QEMUFileGetBufferFunc *get_buffer; - QEMUFileCloseFunc *close; - QEMUFileSetBlocking *set_blocking; - QEMUFileWritevBufferFunc *writev_buffer; - QEMURetPathFunc *get_return_path; - QEMUFileShutdownFunc *shut_down; -} QEMUFileOps; +typedef 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; @@ -120,12 +62,41 @@ typedef struct QEMUFileHooks { QEMURamSaveFunc *save_page; } QEMUFileHooks; -QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops, bool has_ioc); +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_get_fd(QEMUFile *f); int qemu_fclose(QEMUFile *f); -int64_t qemu_ftell(QEMUFile *f); -int64_t qemu_ftell_fast(QEMUFile *f); + +/* + * qemu_file_total_transferred: + * + * Report the total number of bytes transferred with + * this file. + * + * For writable files, any pending buffers will be + * flushed, so the reported value will be equal to + * the number of bytes transferred on the wire. + * + * For readable files, the reported value will be + * equal to the number of bytes transferred on the + * wire. + * + * Returns: the total bytes transferred + */ +int64_t qemu_file_total_transferred(QEMUFile *f); + +/* + * qemu_file_total_transferred_fast: + * + * As qemu_file_total_transferred except for writable + * files, where no flush is performed and the reported + * amount will include the size of any queued buffers, + * on top of the amount actually transferred. + * + * Returns: the total bytes transferred and queued + */ +int64_t qemu_file_total_transferred_fast(QEMUFile *f); + /* * put_buffer without copying the buffer. * The buffer should be available till it is sent asynchronously. @@ -150,12 +121,27 @@ int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); */ int qemu_peek_byte(QEMUFile *f, int offset); void qemu_file_skip(QEMUFile *f, int size); -void qemu_update_position(QEMUFile *f, size_t size); +/* + * qemu_file_credit_transfer: + * + * Report on a number of bytes that have been transferred + * out of band from the main file object I/O methods. This + * accounting information tracks the total migration traffic. + */ +void qemu_file_credit_transfer(QEMUFile *f, size_t size); void qemu_file_reset_rate_limit(QEMUFile *f); -void qemu_file_update_transfer(QEMUFile *f, int64_t len); +/* + * qemu_file_acct_rate_limit: + * + * Report on a number of bytes the have been transferred + * out of band from the main file object I/O methods, and + * need to be applied to the rate limiting calcuations + */ +void qemu_file_acct_rate_limit(QEMUFile *f, int64_t len); void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_get_error_obj(QEMUFile *f, Error **errp); +int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp); void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); diff --git a/migration/ram.c b/migration/ram.c index 5f5e37f64dc..b94669ba5dd 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -32,6 +32,7 @@ #include "qemu/bitmap.h" #include "qemu/madvise.h" #include "qemu/main-loop.h" +#include "io/channel-null.h" #include "xbzrle.h" #include "ram.h" #include "migration.h" @@ -295,6 +296,20 @@ struct RAMSrcPageRequest { QSIMPLEQ_ENTRY(RAMSrcPageRequest) next_req; }; +typedef struct { + /* + * Cached ramblock/offset values if preempted. They're only meaningful if + * preempted==true below. + */ + RAMBlock *ram_block; + unsigned long ram_page; + /* + * Whether a postcopy preemption just happened. Will be reset after + * precopy recovered to background migration. + */ + bool preempted; +} PostcopyPreemptState; + /* State of RAM for migration */ struct RAMState { /* QEMUFile used for this migration */ @@ -349,6 +364,14 @@ struct RAMState { /* Queue of outstanding page requests from the destination */ QemuMutex src_page_req_mutex; QSIMPLEQ_HEAD(, RAMSrcPageRequest) src_page_requests; + + /* Postcopy preemption informations */ + PostcopyPreemptState postcopy_preempt_state; + /* + * Current channel we're using on src VM. Only valid if postcopy-preempt + * is enabled. + */ + unsigned int postcopy_channel; }; typedef struct RAMState RAMState; @@ -356,6 +379,11 @@ static RAMState *ram_state; static NotifierWithReturnList precopy_notifier_list; +static void postcopy_preempt_reset(RAMState *rs) +{ + memset(&rs->postcopy_preempt_state, 0, sizeof(PostcopyPreemptState)); +} + /* Whether postcopy has queued requests? */ static bool postcopy_has_request(RAMState *rs) { @@ -406,6 +434,11 @@ static void ram_transferred_add(uint64_t bytes) ram_counters.transferred += bytes; } +void dirty_sync_missed_zero_copy(void) +{ + ram_counters.dirty_sync_missed_zero_copy++; +} + /* used by the search for pages to send */ struct PageSearchStatus { /* Current block being searched */ @@ -414,8 +447,28 @@ struct PageSearchStatus { unsigned long page; /* Set once we wrap around */ bool complete_round; - /* Whether current page is explicitly requested by postcopy */ + /* + * [POSTCOPY-ONLY] Whether current page is explicitly requested by + * postcopy. When set, the request is "urgent" because the dest QEMU + * threads are waiting for us. + */ bool postcopy_requested; + /* + * [POSTCOPY-ONLY] The target channel to use to send current page. + * + * Note: This may _not_ match with the value in postcopy_requested + * above. Let's imagine the case where the postcopy request is exactly + * the page that we're sending in progress during precopy. In this case + * we'll have postcopy_requested set to true but the target channel + * will be the precopy channel (so that we don't split brain on that + * specific page since the precopy channel already contains partial of + * that page data). + * + * Besides that specific use case, postcopy_target_channel should + * always be equal to postcopy_requested, because by default we send + * postcopy pages via postcopy preempt channel. + */ + bool postcopy_target_channel; }; typedef struct PageSearchStatus PageSearchStatus; @@ -457,8 +510,6 @@ static QemuThread *compress_threads; */ static QemuMutex comp_done_lock; static QemuCond comp_done_cond; -/* The empty QEMUFileOps will be used by file in CompressParam */ -static const QEMUFileOps empty_ops = { }; static QEMUFile *decomp_file; static DecompressParam *decomp_param; @@ -469,6 +520,9 @@ static QemuCond decomp_done_cond; static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, ram_addr_t offset, uint8_t *source_buf); +static void postcopy_preempt_restore(RAMState *rs, PageSearchStatus *pss, + bool postcopy_requested); + static void *do_data_compress(void *opaque) { CompressParam *param = opaque; @@ -569,7 +623,8 @@ static int compress_threads_save_setup(void) /* comp_param[i].file is just used as a dummy buffer to save data, * set its ops to empty. */ - comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops, false); + comp_param[i].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); @@ -1489,8 +1544,12 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, */ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) { - /* This is not a postcopy requested page */ + /* + * This is not a postcopy requested page, mark it "not urgent", and use + * precopy channel to send it. + */ pss->postcopy_requested = false; + pss->postcopy_target_channel = RAM_CHANNEL_PRECOPY; pss->page = migration_bitmap_find_dirty(rs, pss->block, pss->page); if (pss->complete_round && pss->block == rs->last_seen_block && @@ -1947,6 +2006,55 @@ void ram_write_tracking_stop(void) } #endif /* defined(__linux__) */ +/* + * Check whether two addr/offset of the ramblock falls onto the same host huge + * page. Returns true if so, false otherwise. + */ +static bool offset_on_same_huge_page(RAMBlock *rb, uint64_t addr1, + uint64_t addr2) +{ + size_t page_size = qemu_ram_pagesize(rb); + + addr1 = ROUND_DOWN(addr1, page_size); + addr2 = ROUND_DOWN(addr2, page_size); + + return addr1 == addr2; +} + +/* + * Whether a previous preempted precopy huge page contains current requested + * page? Returns true if so, false otherwise. + * + * This should really happen very rarely, because it means when we were sending + * during background migration for postcopy we're sending exactly the page that + * some vcpu got faulted on on dest node. When it happens, we probably don't + * need to do much but drop the request, because we know right after we restore + * the precopy stream it'll be serviced. It'll slightly affect the order of + * postcopy requests to be serviced (e.g. it'll be the same as we move current + * request to the end of the queue) but it shouldn't be a big deal. The most + * imporant thing is we can _never_ try to send a partial-sent huge page on the + * POSTCOPY channel again, otherwise that huge page will got "split brain" on + * two channels (PRECOPY, POSTCOPY). + */ +static bool postcopy_preempted_contains(RAMState *rs, RAMBlock *block, + ram_addr_t offset) +{ + PostcopyPreemptState *state = &rs->postcopy_preempt_state; + + /* No preemption at all? */ + if (!state->preempted) { + return false; + } + + /* Not even the same ramblock? */ + if (state->ram_block != block) { + return false; + } + + return offset_on_same_huge_page(block, offset, + state->ram_page << TARGET_PAGE_BITS); +} + /** * get_queued_page: unqueue a page from the postcopy requests * @@ -1964,7 +2072,20 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) block = unqueue_page(rs, &offset); - if (!block) { + if (block) { + /* See comment above postcopy_preempted_contains() */ + if (postcopy_preempted_contains(rs, block, offset)) { + trace_postcopy_preempt_hit(block->idstr, offset); + /* + * If what we preempted previously was exactly what we're + * requesting right now, restore the preempted precopy + * immediately, boosting its priority as it's requested by + * postcopy. + */ + postcopy_preempt_restore(rs, pss, true); + return true; + } + } else { /* * Poll write faults too if background snapshot is enabled; that's * when we have vcpus got blocked by the write protected pages. @@ -1986,7 +2107,9 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) * really rare. */ pss->complete_round = false; + /* Mark it an urgent request, meanwhile using POSTCOPY channel */ pss->postcopy_requested = true; + pss->postcopy_target_channel = RAM_CHANNEL_POSTCOPY; } return !!block; @@ -2180,6 +2303,129 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) return ram_save_page(rs, pss); } +static bool postcopy_needs_preempt(RAMState *rs, PageSearchStatus *pss) +{ + MigrationState *ms = migrate_get_current(); + + /* Not enabled eager preempt? Then never do that. */ + if (!migrate_postcopy_preempt()) { + return false; + } + + /* If the user explicitly disabled breaking of huge page, skip */ + if (!ms->postcopy_preempt_break_huge) { + return false; + } + + /* If the ramblock we're sending is a small page? Never bother. */ + if (qemu_ram_pagesize(pss->block) == TARGET_PAGE_SIZE) { + return false; + } + + /* Not in postcopy at all? */ + if (!migration_in_postcopy()) { + return false; + } + + /* + * If we're already handling a postcopy request, don't preempt as this page + * has got the same high priority. + */ + if (pss->postcopy_requested) { + return false; + } + + /* If there's postcopy requests, then check it up! */ + return postcopy_has_request(rs); +} + +/* Returns true if we preempted precopy, false otherwise */ +static void postcopy_do_preempt(RAMState *rs, PageSearchStatus *pss) +{ + PostcopyPreemptState *p_state = &rs->postcopy_preempt_state; + + trace_postcopy_preempt_triggered(pss->block->idstr, pss->page); + + /* + * Time to preempt precopy. Cache current PSS into preempt state, so that + * after handling the postcopy pages we can recover to it. We need to do + * so because the dest VM will have partial of the precopy huge page kept + * over in its tmp huge page caches; better move on with it when we can. + */ + p_state->ram_block = pss->block; + p_state->ram_page = pss->page; + p_state->preempted = true; +} + +/* Whether we're preempted by a postcopy request during sending a huge page */ +static bool postcopy_preempt_triggered(RAMState *rs) +{ + return rs->postcopy_preempt_state.preempted; +} + +static void postcopy_preempt_restore(RAMState *rs, PageSearchStatus *pss, + bool postcopy_requested) +{ + PostcopyPreemptState *state = &rs->postcopy_preempt_state; + + assert(state->preempted); + + pss->block = state->ram_block; + pss->page = state->ram_page; + + /* Whether this is a postcopy request? */ + pss->postcopy_requested = postcopy_requested; + /* + * When restoring a preempted page, the old data resides in PRECOPY + * slow channel, even if postcopy_requested is set. So always use + * PRECOPY channel here. + */ + pss->postcopy_target_channel = RAM_CHANNEL_PRECOPY; + + trace_postcopy_preempt_restored(pss->block->idstr, pss->page); + + /* Reset preempt state, most importantly, set preempted==false */ + postcopy_preempt_reset(rs); +} + +static void postcopy_preempt_choose_channel(RAMState *rs, PageSearchStatus *pss) +{ + MigrationState *s = migrate_get_current(); + unsigned int channel = pss->postcopy_target_channel; + QEMUFile *next; + + if (channel != rs->postcopy_channel) { + if (channel == RAM_CHANNEL_PRECOPY) { + next = s->to_dst_file; + } else { + next = s->postcopy_qemufile_src; + } + /* Update and cache the current channel */ + rs->f = next; + rs->postcopy_channel = channel; + + /* + * If channel switched, reset last_sent_block since the old sent block + * may not be on the same channel. + */ + rs->last_sent_block = NULL; + + trace_postcopy_preempt_switch_channel(channel); + } + + trace_postcopy_preempt_send_host_page(pss->block->idstr, pss->page); +} + +/* We need to make sure rs->f always points to the default channel elsewhere */ +static void postcopy_preempt_reset_channel(RAMState *rs) +{ + if (migrate_postcopy_preempt() && migration_in_postcopy()) { + rs->postcopy_channel = RAM_CHANNEL_PRECOPY; + rs->f = migrate_get_current()->to_dst_file; + trace_postcopy_preempt_reset_channel(); + } +} + /** * ram_save_host_page: save a whole host page * @@ -2211,7 +2457,16 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) return 0; } + if (migrate_postcopy_preempt() && migration_in_postcopy()) { + postcopy_preempt_choose_channel(rs, pss); + } + do { + if (postcopy_needs_preempt(rs, pss)) { + postcopy_do_preempt(rs, pss); + break; + } + /* Check the pages is dirty and if it is send it */ if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { tmppages = ram_save_target_page(rs, pss); @@ -2235,6 +2490,19 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) /* The offset we leave with is the min boundary of host page and block */ pss->page = MIN(pss->page, hostpage_boundary); + /* + * When with postcopy preempt mode, flush the data as soon as possible for + * postcopy requests, because we've already sent a whole huge page, so the + * dst node should already have enough resource to atomically filling in + * the current missing page. + * + * More importantly, when using separate postcopy channel, we must do + * explicit flush or it won't flush until the buffer is full. + */ + if (migrate_postcopy_preempt() && pss->postcopy_requested) { + qemu_fflush(rs->f); + } + res = ram_save_release_protection(rs, pss, start_page); return (res < 0 ? res : pages); } @@ -2276,8 +2544,17 @@ static int ram_find_and_save_block(RAMState *rs) found = get_queued_page(rs, &pss); if (!found) { - /* priority queue empty, so just search for something dirty */ - found = find_dirty_block(rs, &pss, &again); + /* + * Recover previous precopy ramblock/offset if postcopy has + * preempted precopy. Otherwise find the next dirty bit. + */ + if (postcopy_preempt_triggered(rs)) { + postcopy_preempt_restore(rs, &pss, false); + found = true; + } else { + /* priority queue empty, so just search for something dirty */ + found = find_dirty_block(rs, &pss, &again); + } } if (found) { @@ -2300,7 +2577,7 @@ void acct_update_position(QEMUFile *f, size_t size, bool zero) } else { ram_counters.normal += pages; ram_transferred_add(size); - qemu_update_position(f, size); + qemu_file_credit_transfer(f, size); } } @@ -2405,6 +2682,8 @@ static void ram_state_reset(RAMState *rs) rs->last_page = 0; rs->last_version = ram_list.version; rs->xbzrle_enabled = false; + postcopy_preempt_reset(rs); + rs->postcopy_channel = RAM_CHANNEL_PRECOPY; } #define MAX_WAIT 50 /* ms, half buffered_file limit */ @@ -3048,6 +3327,8 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) } qemu_mutex_unlock(&rs->bitmap_mutex); + postcopy_preempt_reset_channel(rs); + /* * Must occur before EOS (or any QEMUFile operation) * because of RDMA protocol. @@ -3125,6 +3406,8 @@ static int ram_save_complete(QEMUFile *f, void *opaque) return ret; } + postcopy_preempt_reset_channel(rs); + ret = multifd_send_sync_main(rs->f); if (ret < 0) { return ret; @@ -3209,11 +3492,13 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) * @mis: the migration incoming state pointer * @f: QEMUFile where to read the data from * @flags: Page flags (mostly to see if it's a continuation of previous block) + * @channel: the channel we're using */ static inline RAMBlock *ram_block_from_stream(MigrationIncomingState *mis, - QEMUFile *f, int flags) + QEMUFile *f, int flags, + int channel) { - RAMBlock *block = mis->last_recv_block; + RAMBlock *block = mis->last_recv_block[channel]; char id[256]; uint8_t len; @@ -3240,7 +3525,7 @@ static inline RAMBlock *ram_block_from_stream(MigrationIncomingState *mis, return NULL; } - mis->last_recv_block = block; + mis->last_recv_block[channel] = block; return block; } @@ -3659,15 +3944,15 @@ int ram_postcopy_incoming_init(MigrationIncomingState *mis) * rcu_read_lock is taken prior to this being called. * * @f: QEMUFile where to send the data + * @channel: the channel to use for loading */ -int ram_load_postcopy(QEMUFile *f) +int ram_load_postcopy(QEMUFile *f, int channel) { int flags = 0, ret = 0; bool place_needed = false; bool matches_target_page_size = false; MigrationIncomingState *mis = migration_incoming_get_current(); - /* Currently we only use channel 0. TODO: use all the channels */ - PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[0]; + PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[channel]; while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr; @@ -3691,10 +3976,10 @@ int ram_load_postcopy(QEMUFile *f) flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; - trace_ram_load_postcopy_loop((uint64_t)addr, flags); + trace_ram_load_postcopy_loop(channel, (uint64_t)addr, flags); if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE)) { - block = ram_block_from_stream(mis, f, flags); + block = ram_block_from_stream(mis, f, flags, channel); if (!block) { ret = -EINVAL; break; @@ -3732,10 +4017,10 @@ int ram_load_postcopy(QEMUFile *f) } else if (tmp_page->host_addr != host_page_from_ram_block_offset(block, addr)) { /* not the 1st TP within the HP */ - error_report("Non-same host page detected. " + error_report("Non-same host page detected on channel %d: " "Target host page %p, received host page %p " "(rb %s offset 0x"RAM_ADDR_FMT" target_pages %d)", - tmp_page->host_addr, + channel, tmp_page->host_addr, host_page_from_ram_block_offset(block, addr), block->idstr, addr, tmp_page->target_pages); ret = -EINVAL; @@ -3945,7 +4230,8 @@ static int ram_load_precopy(QEMUFile *f) if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { - RAMBlock *block = ram_block_from_stream(mis, f, flags); + RAMBlock *block = ram_block_from_stream(mis, f, flags, + RAM_CHANNEL_PRECOPY); host = host_from_ram_block_offset(block, addr); /* @@ -4122,7 +4408,12 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) */ WITH_RCU_READ_LOCK_GUARD() { if (postcopy_running) { - ret = ram_load_postcopy(f); + /* + * Note! Here RAM_CHANNEL_PRECOPY is the precopy channel of + * postcopy migration, we have another RAM_CHANNEL_POSTCOPY to + * service fast page faults. + */ + ret = ram_load_postcopy(f, RAM_CHANNEL_PRECOPY); } else { ret = ram_load_precopy(f); } @@ -4284,6 +4575,12 @@ static int ram_resume_prepare(MigrationState *s, void *opaque) return 0; } +void postcopy_preempt_shutdown_file(MigrationState *s) +{ + qemu_put_be64(s->postcopy_qemufile_src, RAM_SAVE_FLAG_EOS); + qemu_fflush(s->postcopy_qemufile_src); +} + static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, diff --git a/migration/ram.h b/migration/ram.h index ded0a3a0866..c7af65ac741 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -61,7 +61,7 @@ void ram_postcopy_send_discard_bitmap(MigrationState *ms); /* For incoming postcopy discard */ int ram_discard_range(const char *block_name, uint64_t start, size_t length); int ram_postcopy_incoming_init(MigrationIncomingState *mis); -int ram_load_postcopy(QEMUFile *f); +int ram_load_postcopy(QEMUFile *f, int channel); void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); @@ -73,6 +73,8 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start); +void postcopy_preempt_shutdown_file(MigrationState *s); +void *postcopy_preempt_thread(void *opaque); /* ram cache */ int colo_init_ram_cache(void); @@ -87,4 +89,6 @@ void ram_write_tracking_prepare(void); int ram_write_tracking_start(void); void ram_write_tracking_stop(void); +void dirty_sync_missed_zero_copy(void); + #endif diff --git a/migration/rdma.c b/migration/rdma.c index 672d1958a92..94a55dd95b5 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -21,7 +21,6 @@ #include "migration.h" #include "qemu-file.h" #include "ram.h" -#include "qemu-file-channel.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -1370,30 +1369,6 @@ const char *print_wrid(int wrid) return wrid_desc[wrid]; } -/* - * RDMA requires memory registration (mlock/pinning), but this is not good for - * overcommitment. - * - * In preparation for the future where LRU information or workload-specific - * writable writable working set memory access behavior is available to QEMU - * it would be nice to have in place the ability to UN-register/UN-pin - * particular memory regions from the RDMA hardware when it is determine that - * those regions of memory will likely not be accessed again in the near future. - * - * While we do not yet have such information right now, the following - * compile-time option allows us to perform a non-optimized version of this - * behavior. - * - * By uncommenting this option, you will cause *all* RDMA transfers to be - * unregistered immediately after the transfer completes on both sides of the - * connection. This has no effect in 'rdma-pin-all' mode, only regular mode. - * - * This will have a terrible impact on migration performance, so until future - * workload information or LRU information is available, do not attempt to use - * this feature except for basic testing. - */ -/* #define RDMA_UNREGISTRATION_EXAMPLE */ - /* * Perform a non-optimized memory unregistration after every transfer * for demonstration purposes, only if pin-all is not requested. @@ -1486,34 +1461,6 @@ static uint64_t qemu_rdma_make_wrid(uint64_t wr_id, uint64_t index, return result; } -/* - * Set bit for unregistration in the next iteration. - * We cannot transmit right here, but will unpin later. - */ -static void qemu_rdma_signal_unregister(RDMAContext *rdma, uint64_t index, - uint64_t chunk, uint64_t wr_id) -{ - if (rdma->unregistrations[rdma->unregister_next] != 0) { - error_report("rdma migration: queue is full"); - } else { - RDMALocalBlock *block = &(rdma->local_ram_blocks.block[index]); - - if (!test_and_set_bit(chunk, block->unregister_bitmap)) { - trace_qemu_rdma_signal_unregister_append(chunk, - rdma->unregister_next); - - rdma->unregistrations[rdma->unregister_next++] = - qemu_rdma_make_wrid(wr_id, index, chunk); - - if (rdma->unregister_next == RDMA_SIGNALED_SEND_MAX) { - rdma->unregister_next = 0; - } - } else { - trace_qemu_rdma_signal_unregister_already(chunk); - } - } -} - /* * Consult the connection manager to see a work request * (of any kind) has completed. @@ -1571,18 +1518,6 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, if (rdma->nb_sent > 0) { rdma->nb_sent--; } - - if (!rdma->pin_all) { - /* - * FYI: If one wanted to signal a specific chunk to be unregistered - * using LRU or workload-specific information, this is the function - * you would call to do so. That chunk would then get asynchronously - * unregistered later. - */ -#ifdef RDMA_UNREGISTRATION_EXAMPLE - qemu_rdma_signal_unregister(rdma, index, chunk, wc.wr_id); -#endif - } } else { trace_qemu_rdma_poll_other(print_wrid(wr_id), wr_id, rdma->nb_sent); } @@ -2137,11 +2072,6 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, chunk_end = ram_chunk_end(block, chunk + chunks); - if (!rdma->pin_all) { -#ifdef RDMA_UNREGISTRATION_EXAMPLE - qemu_rdma_unregister_waiting(rdma); -#endif - } while (test_bit(chunk, block->transit_bitmap)) { (void)count; @@ -3278,33 +3208,17 @@ qio_channel_rdma_shutdown(QIOChannel *ioc, * Offset is an offset to be added to block_offset and used * to also lookup the corresponding RAMBlock. * - * @size > 0 : - * Initiate an transfer this size. - * - * @size == 0 : - * A 'hint' or 'advice' that means that we wish to speculatively - * and asynchronously unregister this memory. In this case, there is no - * guarantee that the unregister will actually happen, for example, - * if the memory is being actively transmitted. Additionally, the memory - * may be re-registered at any future time if a write within the same - * chunk was requested again, even if you attempted to unregister it - * here. - * - * @size < 0 : TODO, not yet supported - * Unregister the memory NOW. This means that the caller does not - * expect there to be any future RDMA transfers and we just want to clean - * things up. This is used in case the upper layer owns the memory and - * cannot wait for qemu_fclose() to occur. + * @size : Number of bytes to transfer * * @bytes_sent : User-specificed pointer to indicate how many bytes were * sent. Usually, this will not be more than a few bytes of * the protocol because most transfers are sent asynchronously. */ -static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, +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) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); RDMAContext *rdma; int ret; @@ -3323,61 +3237,27 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, qemu_fflush(f); - if (size > 0) { - /* - * Add this page to the current 'chunk'. If the chunk - * is full, or the page doesn't belong to the current chunk, - * an actual RDMA write will occur and a new chunk will be formed. - */ - ret = qemu_rdma_write(f, rdma, block_offset, offset, size); - if (ret < 0) { - error_report("rdma migration: write error! %d", ret); - goto err; - } - - /* - * We always return 1 bytes because the RDMA - * protocol is completely asynchronous. We do not yet know - * whether an identified chunk is zero or not because we're - * waiting for other pages to potentially be merged with - * the current chunk. So, we have to call qemu_update_position() - * later on when the actual write occurs. - */ - if (bytes_sent) { - *bytes_sent = 1; - } - } else { - uint64_t index, chunk; - - /* TODO: Change QEMUFileOps prototype to be signed: size_t => long - if (size < 0) { - ret = qemu_rdma_drain_cq(f, rdma); - if (ret < 0) { - fprintf(stderr, "rdma: failed to synchronously drain" - " completion queue before unregistration.\n"); - goto err; - } - } - */ - - ret = qemu_rdma_search_ram_block(rdma, block_offset, - offset, size, &index, &chunk); - - if (ret) { - error_report("ram block search failed"); - goto err; - } - - qemu_rdma_signal_unregister(rdma, index, chunk, 0); + /* + * Add this page to the current 'chunk'. If the chunk + * is full, or the page doesn't belong to the current chunk, + * an actual RDMA write will occur and a new chunk will be formed. + */ + ret = qemu_rdma_write(f, rdma, block_offset, offset, size); + if (ret < 0) { + error_report("rdma migration: write error! %d", ret); + goto err; + } - /* - * TODO: Synchronous, guaranteed unregistration (should not occur during - * fast-path). Otherwise, unregisters will process on the next call to - * qemu_rdma_drain_cq() - if (size < 0) { - qemu_rdma_unregister_waiting(rdma); - } - */ + /* + * 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; } /* @@ -3950,14 +3830,15 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) return 0; } -static int rdma_load_hook(QEMUFile *f, void *opaque, uint64_t flags, void *data) +static int rdma_load_hook(QEMUFile *f, uint64_t flags, void *data) { + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); switch (flags) { case RAM_CONTROL_BLOCK_REG: - return rdma_block_notification_handle(opaque, data); + return rdma_block_notification_handle(rioc, data); case RAM_CONTROL_HOOK: - return qemu_rdma_registration_handle(f, opaque); + return qemu_rdma_registration_handle(f, rioc); default: /* Shouldn't be called with any other values */ @@ -3965,10 +3846,10 @@ static int rdma_load_hook(QEMUFile *f, void *opaque, uint64_t flags, void *data) } } -static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, +static int qemu_rdma_registration_start(QEMUFile *f, uint64_t flags, void *data) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); RDMAContext *rdma; RCU_READ_LOCK_GUARD(); @@ -3994,10 +3875,10 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, * Inform dest that dynamic registrations are done for now. * First, flush writes, if any. */ -static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, +static int qemu_rdma_registration_stop(QEMUFile *f, uint64_t flags, void *data) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); RDMAContext *rdma; RDMAControlHeader head = { .len = 0, .repeat = 1 }; int ret = 0; @@ -4170,12 +4051,12 @@ static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode) rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA)); if (mode[0] == 'w') { - rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc)); + rioc->file = qemu_file_new_output(QIO_CHANNEL(rioc)); rioc->rdmaout = rdma; rioc->rdmain = rdma->return_path; qemu_file_set_hooks(rioc->file, &rdma_write_hooks); } else { - rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc)); + 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); diff --git a/migration/savevm.c b/migration/savevm.c index d9076897b8f..48e85c052c2 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -35,8 +35,8 @@ #include "migration/misc.h" #include "migration/register.h" #include "migration/global_state.h" +#include "migration/channel-block.h" #include "ram.h" -#include "qemu-file-channel.h" #include "qemu-file.h" #include "savevm.h" #include "postcopy-ram.h" @@ -130,48 +130,13 @@ static struct mig_cmd_args { /***********************************************************/ /* savevm/loadvm support */ -static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos, Error **errp) -{ - int ret; - QEMUIOVector qiov; - - qemu_iovec_init_external(&qiov, iov, iovcnt); - ret = bdrv_writev_vmstate(opaque, &qiov, pos); - if (ret < 0) { - return ret; - } - - return qiov.size; -} - -static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, - size_t size, Error **errp) -{ - return bdrv_load_vmstate(opaque, buf, pos, size); -} - -static int bdrv_fclose(void *opaque, Error **errp) -{ - return bdrv_flush(opaque); -} - -static const QEMUFileOps bdrv_read_ops = { - .get_buffer = block_get_buffer, - .close = bdrv_fclose -}; - -static const QEMUFileOps bdrv_write_ops = { - .writev_buffer = block_writev_buffer, - .close = bdrv_fclose -}; - static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) { if (is_writable) { - return qemu_fopen_ops(bs, &bdrv_write_ops, false); + return qemu_file_new_output(QIO_CHANNEL(qio_channel_block_new(bs))); + } else { + return qemu_file_new_input(QIO_CHANNEL(qio_channel_block_new(bs))); } - return qemu_fopen_ops(bs, &bdrv_read_ops, false); } @@ -916,9 +881,9 @@ static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, { int64_t old_offset, size; - old_offset = qemu_ftell_fast(f); + old_offset = qemu_file_total_transferred_fast(f); se->ops->save_state(f, se->opaque); - size = qemu_ftell_fast(f) - old_offset; + size = qemu_file_total_transferred_fast(f) - old_offset; if (vmdesc) { json_writer_int64(vmdesc, "size", size); @@ -2152,6 +2117,13 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) */ qemu_sem_post(&mis->postcopy_pause_sem_fault); + if (migrate_postcopy_preempt()) { + /* The channel should already be setup again; make sure of it */ + assert(mis->postcopy_qemufile_dst); + /* Kick the fast ram load thread too */ + qemu_sem_post(&mis->postcopy_pause_sem_fast_load); + } + return 0; } @@ -2193,7 +2165,7 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) bioc->usage += length; trace_loadvm_handle_cmd_packaged_received(ret); - QEMUFile *packf = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); + QEMUFile *packf = qemu_file_new_input(QIO_CHANNEL(bioc)); ret = qemu_loadvm_state_main(packf, mis); trace_loadvm_handle_cmd_packaged_main(ret); @@ -2575,16 +2547,6 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) { int i; - /* - * If network is interrupted, any temp page we received will be useless - * because we didn't mark them as "received" in receivedmap. After a - * proper recovery later (which will sync src dirty bitmap with receivedmap - * on dest) these cached small pages will be resent again. - */ - for (i = 0; i < mis->postcopy_channels; i++) { - postcopy_temp_page_reset(&mis->postcopy_tmp_pages[i]); - } - trace_postcopy_pause_incoming(); assert(migrate_postcopy_ram()); @@ -2607,12 +2569,37 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) mis->to_src_file = NULL; qemu_mutex_unlock(&mis->rp_mutex); + /* + * NOTE: this must happen before reset the PostcopyTmpPages below, + * otherwise it's racy to reset those fields when the fast load thread + * can be accessing it in parallel. + */ + if (mis->postcopy_qemufile_dst) { + qemu_file_shutdown(mis->postcopy_qemufile_dst); + /* Take the mutex to make sure the fast ram load thread halted */ + qemu_mutex_lock(&mis->postcopy_prio_thread_mutex); + migration_ioc_unregister_yank_from_file(mis->postcopy_qemufile_dst); + qemu_fclose(mis->postcopy_qemufile_dst); + mis->postcopy_qemufile_dst = NULL; + qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); + } + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, MIGRATION_STATUS_POSTCOPY_PAUSED); /* Notify the fault thread for the invalidated file handle */ postcopy_fault_thread_notify(mis); + /* + * If network is interrupted, any temp page we received will be useless + * because we didn't mark them as "received" in receivedmap. After a + * proper recovery later (which will sync src dirty bitmap with receivedmap + * on dest) these cached small pages will be resent again. + */ + for (i = 0; i < mis->postcopy_channels; i++) { + postcopy_temp_page_reset(&mis->postcopy_tmp_pages[i]); + } + error_report("Detected IO failure for postcopy. " "Migration paused."); @@ -2634,8 +2621,8 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) while (true) { section_type = qemu_get_byte(f); - if (qemu_file_get_error(f)) { - ret = qemu_file_get_error(f); + ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, NULL); + if (ret) { break; } @@ -2887,7 +2874,7 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, goto the_end; } ret = qemu_savevm_state(f, errp); - vm_state_size = qemu_ftell(f); + vm_state_size = qemu_file_total_transferred(f); ret2 = qemu_fclose(f); if (ret < 0) { goto the_end; @@ -2951,7 +2938,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, goto the_end; } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-save-state"); - f = qemu_fopen_channel_output(QIO_CHANNEL(ioc)); + f = qemu_file_new_output(QIO_CHANNEL(ioc)); object_unref(OBJECT(ioc)); ret = qemu_save_device_state(f); if (ret < 0 || qemu_fclose(f) < 0) { @@ -2998,7 +2985,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) return; } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state"); - f = qemu_fopen_channel_input(QIO_CHANNEL(ioc)); + f = qemu_file_new_input(QIO_CHANNEL(ioc)); object_unref(OBJECT(ioc)); ret = qemu_loadvm_state(f); diff --git a/migration/socket.c b/migration/socket.c index 4fd5e85f50e..e6fdf3c5e1a 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -26,7 +26,7 @@ #include "io/channel-socket.h" #include "io/net-listener.h" #include "trace.h" - +#include "postcopy-ram.h" struct SocketOutgoingArgs { SocketAddress *saddr; @@ -39,6 +39,24 @@ 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 */ @@ -166,6 +184,8 @@ socket_start_incoming_migration_internal(SocketAddress *saddr, if (migrate_use_multifd()) { num = migrate_multifd_channels(); + } else if (migrate_postcopy_preempt()) { + num = RAM_CHANNEL_MAX; } if (qio_net_listener_open_sync(listener, saddr, num, errp) < 0) { diff --git a/migration/socket.h b/migration/socket.h index 891dbcccebb..dc54df4e6cc 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -21,6 +21,7 @@ #include "io/task.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); diff --git a/migration/tls.c b/migration/tls.c index 32c384a8b65..73e8c9d3c2e 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -166,3 +166,12 @@ void migration_tls_channel_connect(MigrationState *s, NULL, NULL); } + +bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) +{ + if (!migrate_use_tls()) { + return false; + } + + return !object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS); +} diff --git a/migration/tls.h b/migration/tls.h index de4fe2cafd7..98e23c9b0e4 100644 --- a/migration/tls.h +++ b/migration/tls.h @@ -37,4 +37,8 @@ void migration_tls_channel_connect(MigrationState *s, QIOChannel *ioc, const char *hostname, Error **errp); + +/* Whether the QIO channel requires further TLS handshake? */ +bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); + #endif diff --git a/migration/trace-events b/migration/trace-events index 1aec580e92a..a34afe7b857 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -91,7 +91,7 @@ migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned migration_throttle(void) "" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" ram_load_loop(const char *rbname, uint64_t addr, int flags, void *host) "%s: addr: 0x%" PRIx64 " flags: 0x%x host: %p" -ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" +ram_load_postcopy_loop(int channel, uint64_t addr, int flags) "chan=%d addr=0x%" PRIx64 " flags=0x%x" ram_postcopy_send_discard_bitmap(void) "" ram_save_page(const char *rbname, uint64_t offset, void *host) "%s: offset: 0x%" PRIx64 " host: %p" ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: 0x%zx len: 0x%zx" @@ -111,6 +111,12 @@ ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRI ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" ram_write_tracking_ramblock_stop(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" unqueue_page(char *block, uint64_t offset, bool dirty) "ramblock '%s' offset 0x%"PRIx64" dirty %d" +postcopy_preempt_triggered(char *str, unsigned long page) "during sending ramblock %s offset 0x%lx" +postcopy_preempt_restored(char *str, unsigned long page) "ramblock %s offset 0x%lx" +postcopy_preempt_hit(char *str, uint64_t offset) "ramblock %s offset 0x%"PRIx64 +postcopy_preempt_send_host_page(char *str, uint64_t offset) "ramblock %s offset 0x%"PRIx64 +postcopy_preempt_switch_channel(int channel) "%d" +postcopy_preempt_reset_channel(void) "" # multifd.c multifd_new_send_channel_async(uint8_t id) "channel %u" @@ -176,6 +182,7 @@ migration_thread_low_pending(uint64_t pending) "%" PRIu64 migrate_transferred(uint64_t tranferred, uint64_t time_spent, uint64_t bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " max_size %" PRId64 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" # channel.c migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" @@ -263,6 +270,8 @@ mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, i mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" postcopy_pause_fault_thread(void) "" postcopy_pause_fault_thread_continued(void) "" +postcopy_pause_fast_load(void) "" +postcopy_pause_fast_load_continued(void) "" postcopy_ram_fault_thread_entry(void) "" postcopy_ram_fault_thread_exit(void) "" postcopy_ram_fault_thread_fds_core(int baseufd, int quitfd) "ufd: %d quitfd: %d" @@ -278,6 +287,10 @@ postcopy_request_shared_page(const char *sharer, const char *rb, uint64_t rb_off postcopy_request_shared_page_present(const char *sharer, const char *rb, uint64_t rb_offset) "%s already %s offset 0x%"PRIx64 postcopy_wake_shared(uint64_t client_addr, const char *rb) "at 0x%"PRIx64" in %s" postcopy_page_req_del(void *addr, int count) "resolved page req %p total %d" +postcopy_preempt_tls_handshake(void) "" +postcopy_preempt_new_channel(void) "" +postcopy_preempt_thread_entry(void) "" +postcopy_preempt_thread_exit(void) "" get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" diff --git a/migration/vmstate.c b/migration/vmstate.c index 36ae8b9e191..924494bda3c 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -360,7 +360,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *curr_elem = first_elem + size * i; vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); - old_offset = qemu_ftell_fast(f); + old_offset = qemu_file_total_transferred_fast(f); if (field->flags & VMS_ARRAY_OF_POINTER) { assert(curr_elem); curr_elem = *(void **)curr_elem; @@ -390,7 +390,8 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, return ret; } - written_bytes = qemu_ftell_fast(f) - old_offset; + written_bytes = qemu_file_total_transferred_fast(f) - + old_offset; vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); /* Compressed arrays only care about the first element */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 622c783c32e..c6cd6f91dde 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -40,6 +40,7 @@ #include "qapi/qapi-commands-pci.h" #include "qapi/qapi-commands-rocker.h" #include "qapi/qapi-commands-run-state.h" +#include "qapi/qapi-commands-stats.h" #include "qapi/qapi-commands-tpm.h" #include "qapi/qapi-commands-ui.h" #include "qapi/qapi-visit-net.h" @@ -52,6 +53,7 @@ #include "ui/console.h" #include "qemu/cutils.h" #include "qemu/error-report.h" +#include "hw/core/cpu.h" #include "hw/intc/intc.h" #include "migration/snapshot.h" #include "migration/misc.h" @@ -305,6 +307,11 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", info->ram->postcopy_bytes >> 10); } + if (info->ram->dirty_sync_missed_zero_copy) { + monitor_printf(mon, + "Zero-copy-send fallbacks happened: %" PRIu64 " times\n", + info->ram->dirty_sync_missed_zero_copy); + } } if (info->has_disk) { @@ -1309,12 +1316,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_multifd_zstd_level = true; visit_type_uint8(v, param, &p->multifd_zstd_level, &err); break; -#ifdef CONFIG_LINUX - case MIGRATION_PARAMETER_ZERO_COPY_SEND: - p->has_zero_copy_send = true; - visit_type_bool(v, param, &p->zero_copy_send, &err); - break; -#endif case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: p->has_xbzrle_cache_size = true; if (!visit_type_size(v, param, &cache_size, &err)) { @@ -2239,3 +2240,235 @@ void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) } hmp_handle_error(mon, err); } + +static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) +{ + const char *unit = NULL; + monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type), + value->has_unit || value->exponent ? ", " : ""); + + if (value->has_unit) { + if (value->unit == STATS_UNIT_SECONDS) { + unit = "s"; + } else if (value->unit == STATS_UNIT_BYTES) { + unit = "B"; + } + } + + if (unit && value->base == 10 && + value->exponent >= -18 && value->exponent <= 18 && + value->exponent % 3 == 0) { + monitor_printf(mon, "%s", si_prefix(value->exponent)); + } else if (unit && value->base == 2 && + value->exponent >= 0 && value->exponent <= 60 && + value->exponent % 10 == 0) { + + monitor_printf(mon, "%s", iec_binary_prefix(value->exponent)); + } else if (value->exponent) { + /* Use exponential notation and write the unit's English name */ + monitor_printf(mon, "* %d^%d%s", + value->base, value->exponent, + value->has_unit ? " " : ""); + unit = NULL; + } + + if (value->has_unit) { + monitor_printf(mon, "%s", unit ? unit : StatsUnit_str(value->unit)); + } + + /* Print bucket size for linear histograms */ + if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) { + monitor_printf(mon, ", bucket size=%d", value->bucket_size); + } + monitor_printf(mon, ")"); +} + +static StatsSchemaValueList *find_schema_value_list( + StatsSchemaList *list, StatsProvider provider, + StatsTarget target) +{ + StatsSchemaList *node; + + for (node = list; node; node = node->next) { + if (node->value->provider == provider && + node->value->target == target) { + return node->value->stats; + } + } + return NULL; +} + +static void print_stats_results(Monitor *mon, StatsTarget target, + bool show_provider, + StatsResult *result, + StatsSchemaList *schema) +{ + /* Find provider schema */ + StatsSchemaValueList *schema_value_list = + find_schema_value_list(schema, result->provider, target); + StatsList *stats_list; + + if (!schema_value_list) { + monitor_printf(mon, "failed to find schema list for %s\n", + StatsProvider_str(result->provider)); + return; + } + + if (show_provider) { + monitor_printf(mon, "provider: %s\n", + StatsProvider_str(result->provider)); + } + + for (stats_list = result->stats; stats_list; + stats_list = stats_list->next, + schema_value_list = schema_value_list->next) { + + Stats *stats = stats_list->value; + StatsValue *stats_value = stats->value; + StatsSchemaValue *schema_value = schema_value_list->value; + + /* Find schema entry */ + while (!g_str_equal(stats->name, schema_value->name)) { + if (!schema_value_list->next) { + monitor_printf(mon, "failed to find schema entry for %s\n", + stats->name); + return; + } + schema_value_list = schema_value_list->next; + schema_value = schema_value_list->value; + } + + print_stats_schema_value(mon, schema_value); + + if (stats_value->type == QTYPE_QNUM) { + monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar); + } else if (stats_value->type == QTYPE_QBOOL) { + monitor_printf(mon, ": %s\n", stats_value->u.boolean ? "yes" : "no"); + } else if (stats_value->type == QTYPE_QLIST) { + uint64List *list; + int i; + + monitor_printf(mon, ": "); + for (list = stats_value->u.list, i = 1; + list; + list = list->next, i++) { + monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value); + } + monitor_printf(mon, "\n"); + } + } +} + +/* Create the StatsFilter that is needed for an "info stats" invocation. */ +static StatsFilter *stats_filter(StatsTarget target, const char *names, + int cpu_index, StatsProvider provider) +{ + StatsFilter *filter = g_malloc0(sizeof(*filter)); + StatsProvider provider_idx; + StatsRequestList *request_list = NULL; + + filter->target = target; + switch (target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + { + strList *vcpu_list = NULL; + CPUState *cpu = qemu_get_cpu(cpu_index); + char *canonical_path = object_get_canonical_path(OBJECT(cpu)); + + QAPI_LIST_PREPEND(vcpu_list, canonical_path); + filter->u.vcpu.has_vcpus = true; + filter->u.vcpu.vcpus = vcpu_list; + break; + } + default: + break; + } + + if (!names && provider == STATS_PROVIDER__MAX) { + return filter; + } + + /* + * "info stats" can only query either one or all the providers. Querying + * by name, but not by provider, requires the creation of one filter per + * provider. + */ + for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) { + if (provider == STATS_PROVIDER__MAX || provider == provider_idx) { + StatsRequest *request = g_new0(StatsRequest, 1); + request->provider = provider_idx; + if (names && !g_str_equal(names, "*")) { + request->has_names = true; + request->names = strList_from_comma_list(names); + } + QAPI_LIST_PREPEND(request_list, request); + } + } + + filter->has_providers = true; + filter->providers = request_list; + return filter; +} + +void hmp_info_stats(Monitor *mon, const QDict *qdict) +{ + const char *target_str = qdict_get_str(qdict, "target"); + const char *provider_str = qdict_get_try_str(qdict, "provider"); + const char *names = qdict_get_try_str(qdict, "names"); + + StatsProvider provider = STATS_PROVIDER__MAX; + StatsTarget target; + Error *err = NULL; + g_autoptr(StatsSchemaList) schema = NULL; + g_autoptr(StatsResultList) stats = NULL; + g_autoptr(StatsFilter) filter = NULL; + StatsResultList *entry; + + target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats target %s\n", target_str); + goto exit_no_print; + } + if (provider_str) { + provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats provider %s\n", provider_str); + goto exit_no_print; + } + } + + schema = qmp_query_stats_schemas(provider_str ? true : false, + provider, &err); + if (err) { + goto exit; + } + + switch (target) { + case STATS_TARGET_VM: + filter = stats_filter(target, names, -1, provider); + break; + case STATS_TARGET_VCPU: {} + int cpu_index = monitor_get_cpu_index(mon); + filter = stats_filter(target, names, cpu_index, provider); + break; + default: + abort(); + } + + stats = qmp_query_stats(filter, &err); + if (err) { + goto exit; + } + for (entry = stats; entry; entry = entry->next) { + print_stats_results(mon, target, provider_str == NULL, entry->value, schema); + } + +exit: + if (err) { + monitor_printf(mon, "%s\n", error_get_pretty(err)); + } +exit_no_print: + error_free(err); +} diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 1ebb89f46c1..7314cd813d8 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -35,6 +35,7 @@ #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-stats.h" #include "qapi/qapi-commands-ui.h" #include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" @@ -43,6 +44,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" +#include "monitor/stats.h" NameInfo *qmp_query_name(Error **errp) { @@ -441,3 +443,156 @@ HumanReadableText *qmp_x_query_irq(Error **errp) return human_readable_text_from_str(buf); } + +typedef struct StatsCallbacks { + StatsProvider provider; + StatRetrieveFunc *stats_cb; + SchemaRetrieveFunc *schemas_cb; + QTAILQ_ENTRY(StatsCallbacks) next; +} StatsCallbacks; + +static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks = + QTAILQ_HEAD_INITIALIZER(stats_callbacks); + +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn) +{ + StatsCallbacks *entry = g_new(StatsCallbacks, 1); + entry->provider = provider; + entry->stats_cb = stats_fn; + entry->schemas_cb = schemas_fn; + + QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next); +} + +static bool invoke_stats_cb(StatsCallbacks *entry, + StatsResultList **stats_results, + StatsFilter *filter, StatsRequest *request, + Error **errp) +{ + strList *targets = NULL; + strList *names = NULL; + ERRP_GUARD(); + + if (request) { + if (request->provider != entry->provider) { + return true; + } + if (request->has_names && !request->names) { + return true; + } + names = request->has_names ? request->names : NULL; + } + + switch (filter->target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + if (filter->u.vcpu.has_vcpus) { + if (!filter->u.vcpu.vcpus) { + /* No targets allowed? Return no statistics. */ + return true; + } + targets = filter->u.vcpu.vcpus; + } + break; + default: + abort(); + } + + entry->stats_cb(stats_results, filter->target, names, targets, errp); + if (*errp) { + qapi_free_StatsResultList(*stats_results); + *stats_results = NULL; + return false; + } + return true; +} + +StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp) +{ + StatsResultList *stats_results = NULL; + StatsCallbacks *entry; + StatsRequestList *request; + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (filter->has_providers) { + for (request = filter->providers; request; request = request->next) { + if (!invoke_stats_cb(entry, &stats_results, filter, + request->value, errp)) { + break; + } + } + } else { + if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) { + break; + } + } + } + + return stats_results; +} + +StatsSchemaList *qmp_query_stats_schemas(bool has_provider, + StatsProvider provider, + Error **errp) +{ + StatsSchemaList *stats_results = NULL; + StatsCallbacks *entry; + ERRP_GUARD(); + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (!has_provider || provider == entry->provider) { + entry->schemas_cb(&stats_results, errp); + if (*errp) { + qapi_free_StatsSchemaList(stats_results); + return NULL; + } + } + } + + return stats_results; +} + +void add_stats_entry(StatsResultList **stats_results, StatsProvider provider, + const char *qom_path, StatsList *stats_list) +{ + StatsResult *entry = g_new0(StatsResult, 1); + + entry->provider = provider; + if (qom_path) { + entry->has_qom_path = true; + entry->qom_path = g_strdup(qom_path); + } + entry->stats = stats_list; + + QAPI_LIST_PREPEND(*stats_results, entry); +} + +void add_stats_schema(StatsSchemaList **schema_results, + StatsProvider provider, StatsTarget target, + StatsSchemaValueList *stats_list) +{ + StatsSchema *entry = g_new0(StatsSchema, 1); + + entry->provider = provider; + entry->target = target; + entry->stats = stats_list; + QAPI_LIST_PREPEND(*schema_results, entry); +} + +bool apply_str_list_filter(const char *string, strList *list) +{ + strList *str_list = NULL; + + if (!list) { + return true; + } + for (str_list = list; str_list; str_list = str_list->next) { + if (g_str_equal(string, str_list->value)) { + return true; + } + } + return false; +} diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 2a632931c39..0c5f917efac 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "trace.h" #include "block/nbd.h" @@ -210,6 +211,7 @@ static void *connect_thread_func(void *opaque) object_unref(OBJECT(conn->sioc)); conn->sioc = NULL; if (conn->do_retry && !conn->detached) { + trace_nbd_connect_thread_sleep(timeout); qemu_mutex_unlock(&conn->mutex); sleep(timeout); diff --git a/nbd/server.c b/nbd/server.c index 213e00e761a..ada16089f32 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2039,8 +2039,8 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, stl_be_p(&chunk.length, pnum); ret = nbd_co_send_iov(client, iov, 1, errp); } else { - ret = blk_pread(exp->common.blk, offset + progress, - data + progress, pnum); + ret = blk_pread(exp->common.blk, offset + progress, pnum, + data + progress, 0); if (ret < 0) { error_setg_errno(errp, -ret, "reading from file failed"); break; @@ -2444,7 +2444,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, data, request->len, errp); } - ret = blk_pread(exp->common.blk, request->from, data, request->len); + ret = blk_pread(exp->common.blk, request->from, request->len, data, 0); if (ret < 0) { return nbd_send_generic_reply(client, request->handle, ret, "reading from file failed", errp); @@ -2511,7 +2511,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, if (request->flags & NBD_CMD_FLAG_FUA) { flags |= BDRV_REQ_FUA; } - ret = blk_pwrite(exp->common.blk, request->from, data, request->len, + ret = blk_pwrite(exp->common.blk, request->from, request->len, data, flags); return nbd_send_generic_reply(client, request->handle, ret, "writing to file failed", errp); diff --git a/nbd/trace-events b/nbd/trace-events index c4919a2dd58..b7032ca2778 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -73,3 +73,6 @@ nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *n nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", align=0x%" PRIx32 nbd_trip(void) "Reading request" + +# client-connection.c +nbd_connect_thread_sleep(uint64_t timeout) "timeout %" PRIu64 diff --git a/net/colo-compare.c b/net/colo-compare.c index d5d09658057..787c740f142 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -1323,7 +1323,7 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) s->connection_track_table = g_hash_table_new_full(connection_key_hash, connection_key_equal, g_free, - connection_destroy); + NULL); colo_compare_iothread(s); diff --git a/net/colo.c b/net/colo.c index 1f8162f59f7..6b0ff562add 100644 --- a/net/colo.c +++ b/net/colo.c @@ -46,7 +46,14 @@ int parse_packet_early(Packet *pkt) static const uint8_t vlan[] = {0x81, 0x00}; uint8_t *data = pkt->data + pkt->vnet_hdr_len; uint16_t l3_proto; - ssize_t l2hdr_len = eth_get_l2_hdr_length(data); + ssize_t l2hdr_len; + + if (data == NULL) { + trace_colo_proxy_main_vnet_info("This packet is not parsed correctly, " + "pkt->vnet_hdr_len", pkt->vnet_hdr_len); + return 1; + } + l2hdr_len = eth_get_l2_hdr_length(data); if (pkt->size < ETH_HLEN + pkt->vnet_hdr_len) { trace_colo_proxy_main("pkt->size < ETH_HLEN"); @@ -218,7 +225,7 @@ Connection *connection_get(GHashTable *connection_track_table, /* * clear the conn_list */ - while (!g_queue_is_empty(conn_list)) { + while (conn_list && !g_queue_is_empty(conn_list)) { connection_destroy(g_queue_pop_head(conn_list)); } } diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index bf05023dc3c..c18c4c20190 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -383,7 +383,7 @@ static void colo_rewriter_setup(NetFilterState *nf, Error **errp) s->connection_track_table = g_hash_table_new_full(connection_key_hash, connection_key_equal, g_free, - connection_destroy); + NULL); s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf); } diff --git a/net/meson.build b/net/meson.build index 754e2d1d405..d1be76daf36 100644 --- a/net/meson.build +++ b/net/meson.build @@ -41,7 +41,8 @@ endif softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files(tap_posix)) softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('tap-win32.c')) if have_vhost_net_vdpa - softmmu_ss.add(files('vhost-vdpa.c')) + softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c')) + softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-vdpa-stub.c')) endif vmnet_files = files( diff --git a/net/trace-events b/net/trace-events index d7a17256cca..6af927b4b9b 100644 --- a/net/trace-events +++ b/net/trace-events @@ -9,6 +9,7 @@ vhost_user_event(const char *chr, int event) "chr: %s got event: %d" # colo.c colo_proxy_main(const char *chr) ": %s" +colo_proxy_main_vnet_info(const char *sta, int size) ": %s = %d" # colo-compare.c colo_compare_main(const char *chr) ": %s" diff --git a/net/vhost-vdpa-stub.c b/net/vhost-vdpa-stub.c new file mode 100644 index 00000000000..1732ed2443f --- /dev/null +++ b/net/vhost-vdpa-stub.c @@ -0,0 +1,21 @@ +/* + * vhost-vdpa-stub.c + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "clients.h" +#include "net/vhost-vdpa.h" +#include "qapi/error.h" + +int net_init_vhost_vdpa(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); + return -1; +} diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index df1e69ee723..6abad276a61 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -11,11 +11,14 @@ #include "qemu/osdep.h" #include "clients.h" +#include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" #include "net/vhost-vdpa.h" #include "hw/virtio/vhost-vdpa.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/memalign.h" #include "qemu/option.h" #include "qapi/error.h" #include @@ -30,6 +33,9 @@ typedef struct VhostVDPAState { NetClientState nc; struct vhost_vdpa vhost_vdpa; VHostNetState *vhost_net; + + /* Control commands shadow buffers */ + void *cvq_cmd_out_buffer, *cvq_cmd_in_buffer; bool started; } VhostVDPAState; @@ -69,6 +75,28 @@ const int vdpa_feature_bits[] = { VHOST_INVALID_FEATURE_BIT }; +/** Supported device specific feature bits with SVQ */ +static const uint64_t vdpa_svq_device_features = + BIT_ULL(VIRTIO_NET_F_CSUM) | + BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) | + BIT_ULL(VIRTIO_NET_F_MTU) | + BIT_ULL(VIRTIO_NET_F_MAC) | + BIT_ULL(VIRTIO_NET_F_GUEST_TSO4) | + BIT_ULL(VIRTIO_NET_F_GUEST_TSO6) | + BIT_ULL(VIRTIO_NET_F_GUEST_ECN) | + BIT_ULL(VIRTIO_NET_F_GUEST_UFO) | + BIT_ULL(VIRTIO_NET_F_HOST_TSO4) | + BIT_ULL(VIRTIO_NET_F_HOST_TSO6) | + BIT_ULL(VIRTIO_NET_F_HOST_ECN) | + BIT_ULL(VIRTIO_NET_F_HOST_UFO) | + BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) | + BIT_ULL(VIRTIO_NET_F_STATUS) | + BIT_ULL(VIRTIO_NET_F_CTRL_VQ) | + BIT_ULL(VIRTIO_F_ANY_LAYOUT) | + BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) | + BIT_ULL(VIRTIO_NET_F_RSC_EXT) | + BIT_ULL(VIRTIO_NET_F_STANDBY); + VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); @@ -127,7 +155,13 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, static void vhost_vdpa_cleanup(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + struct vhost_dev *dev = &s->vhost_net->dev; + qemu_vfree(s->cvq_cmd_out_buffer); + qemu_vfree(s->cvq_cmd_in_buffer); + if (dev->vq_index + dev->nvqs == dev->vq_index_end) { + g_clear_pointer(&s->vhost_vdpa.iova_tree, vhost_iova_tree_delete); + } if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); g_free(s->vhost_net); @@ -187,13 +221,251 @@ static NetClientInfo net_vhost_vdpa_info = { .check_peer_type = vhost_vdpa_check_peer_type, }; +static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr) +{ + VhostIOVATree *tree = v->iova_tree; + DMAMap needle = { + /* + * No need to specify size or to look for more translations since + * this contiguous chunk was allocated by us. + */ + .translated_addr = (hwaddr)(uintptr_t)addr, + }; + const DMAMap *map = vhost_iova_tree_find_iova(tree, &needle); + int r; + + if (unlikely(!map)) { + error_report("Cannot locate expected map"); + return; + } + + r = vhost_vdpa_dma_unmap(v, map->iova, map->size + 1); + if (unlikely(r != 0)) { + error_report("Device cannot unmap: %s(%d)", g_strerror(r), r); + } + + vhost_iova_tree_remove(tree, map); +} + +static size_t vhost_vdpa_net_cvq_cmd_len(void) +{ + /* + * MAC_TABLE_SET is the ctrl command that produces the longer out buffer. + * In buffer is always 1 byte, so it should fit here + */ + return sizeof(struct virtio_net_ctrl_hdr) + + 2 * sizeof(struct virtio_net_ctrl_mac) + + MAC_TABLE_ENTRIES * ETH_ALEN; +} + +static size_t vhost_vdpa_net_cvq_cmd_page_len(void) +{ + return ROUND_UP(vhost_vdpa_net_cvq_cmd_len(), qemu_real_host_page_size()); +} + +/** Copy and map a guest buffer. */ +static bool vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, + const struct iovec *out_data, + size_t out_num, size_t data_len, void *buf, + size_t *written, bool write) +{ + DMAMap map = {}; + int r; + + if (unlikely(!data_len)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid legnth of %s buffer\n", + __func__, write ? "in" : "out"); + return false; + } + + *written = iov_to_buf(out_data, out_num, 0, buf, data_len); + map.translated_addr = (hwaddr)(uintptr_t)buf; + map.size = vhost_vdpa_net_cvq_cmd_page_len() - 1; + map.perm = write ? IOMMU_RW : IOMMU_RO, + r = vhost_iova_tree_map_alloc(v->iova_tree, &map); + if (unlikely(r != IOVA_OK)) { + error_report("Cannot map injected element"); + return false; + } + + r = vhost_vdpa_dma_map(v, map.iova, vhost_vdpa_net_cvq_cmd_page_len(), buf, + !write); + if (unlikely(r < 0)) { + goto dma_map_err; + } + + return true; + +dma_map_err: + vhost_iova_tree_remove(v->iova_tree, &map); + return false; +} + +/** + * Copy the guest element into a dedicated buffer suitable to be sent to NIC + * + * @iov: [0] is the out buffer, [1] is the in one + */ +static bool vhost_vdpa_net_cvq_map_elem(VhostVDPAState *s, + VirtQueueElement *elem, + struct iovec *iov) +{ + size_t in_copied; + bool ok; + + iov[0].iov_base = s->cvq_cmd_out_buffer; + ok = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, elem->out_sg, elem->out_num, + vhost_vdpa_net_cvq_cmd_len(), iov[0].iov_base, + &iov[0].iov_len, false); + if (unlikely(!ok)) { + return false; + } + + iov[1].iov_base = s->cvq_cmd_in_buffer; + ok = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, NULL, 0, + sizeof(virtio_net_ctrl_ack), iov[1].iov_base, + &in_copied, true); + if (unlikely(!ok)) { + vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer); + return false; + } + + iov[1].iov_len = sizeof(virtio_net_ctrl_ack); + return true; +} + +/** + * Do not forward commands not supported by SVQ. Otherwise, the device could + * accept it and qemu would not know how to update the device model. + */ +static bool vhost_vdpa_net_cvq_validate_cmd(const struct iovec *out, + size_t out_num) +{ + struct virtio_net_ctrl_hdr ctrl; + size_t n; + + n = iov_to_buf(out, out_num, 0, &ctrl, sizeof(ctrl)); + if (unlikely(n < sizeof(ctrl))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid legnth of out buffer %zu\n", __func__, n); + return false; + } + + switch (ctrl.class) { + case VIRTIO_NET_CTRL_MAC: + switch (ctrl.cmd) { + case VIRTIO_NET_CTRL_MAC_ADDR_SET: + return true; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid mac cmd %u\n", + __func__, ctrl.cmd); + }; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid control class %u\n", + __func__, ctrl.class); + }; + + return false; +} + +/** + * Validate and copy control virtqueue commands. + * + * Following QEMU guidelines, we offer a copy of the buffers to the device to + * prevent TOCTOU bugs. + */ +static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq, + VirtQueueElement *elem, + void *opaque) +{ + VhostVDPAState *s = opaque; + size_t in_len, dev_written; + virtio_net_ctrl_ack status = VIRTIO_NET_ERR; + /* out and in buffers sent to the device */ + struct iovec dev_buffers[2] = { + { .iov_base = s->cvq_cmd_out_buffer }, + { .iov_base = s->cvq_cmd_in_buffer }, + }; + /* in buffer used for device model */ + const struct iovec in = { + .iov_base = &status, + .iov_len = sizeof(status), + }; + int r = -EINVAL; + bool ok; + + ok = vhost_vdpa_net_cvq_map_elem(s, elem, dev_buffers); + if (unlikely(!ok)) { + goto out; + } + + ok = vhost_vdpa_net_cvq_validate_cmd(&dev_buffers[0], 1); + if (unlikely(!ok)) { + goto out; + } + + r = vhost_svq_add(svq, &dev_buffers[0], 1, &dev_buffers[1], 1, elem); + if (unlikely(r != 0)) { + if (unlikely(r == -ENOSPC)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n", + __func__); + } + goto out; + } + + /* + * 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 + */ + dev_written = vhost_svq_poll(svq); + if (unlikely(dev_written < sizeof(status))) { + error_report("Insufficient written data (%zu)", dev_written); + goto out; + } + + memcpy(&status, dev_buffers[1].iov_base, sizeof(status)); + if (status != VIRTIO_NET_OK) { + goto out; + } + + status = VIRTIO_NET_ERR; + virtio_net_handle_ctrl_iov(svq->vdev, &in, 1, dev_buffers, 1); + if (status != VIRTIO_NET_OK) { + error_report("Bad CVQ processing in model"); + } + +out: + in_len = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, + sizeof(status)); + if (unlikely(in_len < sizeof(status))) { + error_report("Bad device CVQ written length"); + } + vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status))); + g_free(elem); + if (dev_buffers[0].iov_base) { + vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, dev_buffers[0].iov_base); + } + if (dev_buffers[1].iov_base) { + vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, dev_buffers[1].iov_base); + } + return r; +} + +static const VhostShadowVirtqueueOps vhost_vdpa_net_svq_ops = { + .avail_handler = vhost_vdpa_net_handle_ctrl_avail, +}; + static NetClientState *net_vhost_vdpa_init(NetClientState *peer, const char *device, const char *name, int vdpa_device_fd, int queue_pair_index, int nvqs, - bool is_datapath) + bool is_datapath, + bool svq, + VhostIOVATree *iova_tree) { NetClientState *nc = NULL; VhostVDPAState *s; @@ -211,6 +483,21 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, s->vhost_vdpa.device_fd = vdpa_device_fd; s->vhost_vdpa.index = queue_pair_index; + s->vhost_vdpa.shadow_vqs_enabled = svq; + s->vhost_vdpa.iova_tree = iova_tree; + if (!is_datapath) { + s->cvq_cmd_out_buffer = qemu_memalign(qemu_real_host_page_size(), + vhost_vdpa_net_cvq_cmd_page_len()); + memset(s->cvq_cmd_out_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len()); + s->cvq_cmd_in_buffer = qemu_memalign(qemu_real_host_page_size(), + vhost_vdpa_net_cvq_cmd_page_len()); + memset(s->cvq_cmd_in_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len()); + + s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops; + s->vhost_vdpa.shadow_vq_ops_opaque = s; + error_setg(&s->vhost_vdpa.migration_blocker, + "Migration disabled: vhost-vdpa uses CVQ."); + } ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs); if (ret) { qemu_del_net_client(nc); @@ -219,20 +506,32 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, return nc; } -static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp) +static int vhost_vdpa_get_iova_range(int fd, + struct vhost_vdpa_iova_range *iova_range) +{ + int ret = ioctl(fd, VHOST_VDPA_GET_IOVA_RANGE, iova_range); + + return ret < 0 ? -errno : 0; +} + +static int vhost_vdpa_get_features(int fd, uint64_t *features, Error **errp) +{ + int ret = ioctl(fd, VHOST_GET_FEATURES, features); + if (unlikely(ret < 0)) { + error_setg_errno(errp, errno, + "Fail to query features from vhost-vDPA device"); + } + return ret; +} + +static int vhost_vdpa_get_max_queue_pairs(int fd, uint64_t features, + int *has_cvq, Error **errp) { unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); g_autofree struct vhost_vdpa_config *config = NULL; __virtio16 *max_queue_pairs; - uint64_t features; int ret; - ret = ioctl(fd, VHOST_GET_FEATURES, &features); - if (ret) { - error_setg(errp, "Fail to query features from vhost-vDPA device"); - return ret; - } - if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) { *has_cvq = 1; } else { @@ -262,10 +561,12 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevVhostVDPAOptions *opts; + uint64_t features; int vdpa_device_fd; g_autofree NetClientState **ncs = NULL; + g_autoptr(VhostIOVATree) iova_tree = NULL; NetClientState *nc; - int queue_pairs, i, has_cvq = 0; + int queue_pairs, r, i, has_cvq = 0; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; @@ -279,29 +580,57 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, return -errno; } - queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, + r = vhost_vdpa_get_features(vdpa_device_fd, &features, errp); + if (unlikely(r < 0)) { + return r; + } + + queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, features, &has_cvq, errp); if (queue_pairs < 0) { qemu_close(vdpa_device_fd); return queue_pairs; } + if (opts->x_svq) { + struct vhost_vdpa_iova_range iova_range; + + uint64_t invalid_dev_features = + features & ~vdpa_svq_device_features & + /* Transport are all accepted at this point */ + ~MAKE_64BIT_MASK(VIRTIO_TRANSPORT_F_START, + VIRTIO_TRANSPORT_F_END - VIRTIO_TRANSPORT_F_START); + + if (invalid_dev_features) { + error_setg(errp, "vdpa svq does not work with features 0x%" PRIx64, + invalid_dev_features); + goto err_svq; + } + + vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range); + iova_tree = vhost_iova_tree_new(iova_range.first, iova_range.last); + } + ncs = g_malloc0(sizeof(*ncs) * queue_pairs); for (i = 0; i < queue_pairs; i++) { ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, - vdpa_device_fd, i, 2, true); + vdpa_device_fd, i, 2, true, opts->x_svq, + iova_tree); if (!ncs[i]) goto err; } if (has_cvq) { nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, - vdpa_device_fd, i, 1, false); + vdpa_device_fd, i, 1, false, + opts->x_svq, iova_tree); if (!nc) goto err; } + /* iova_tree ownership belongs to last NetClientState */ + g_steal_pointer(&iova_tree); return 0; err: @@ -310,6 +639,8 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, qemu_del_net_client(ncs[i]); } } + +err_svq: qemu_close(vdpa_device_fd); return -1; diff --git a/pc-bios/keymaps/meson.build b/pc-bios/keymaps/meson.build index 44247a12b54..2837eb34f4e 100644 --- a/pc-bios/keymaps/meson.build +++ b/pc-bios/keymaps/meson.build @@ -40,9 +40,9 @@ else endif cp = find_program('cp') -t = [] -foreach km, args: keymaps - if native_qemu_keymap.found() +if native_qemu_keymap.found() + t = [] + foreach km, args: keymaps # generate with qemu-kvm t += custom_target(km, build_by_default: true, @@ -50,20 +50,11 @@ foreach km, args: keymaps command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], install: true, install_dir: qemu_datadir / 'keymaps') - else - # copy from source tree - t += custom_target(km, - build_by_default: true, - input: km, - output: km, - command: [cp, '@INPUT@', '@OUTPUT@'], - install: true, - install_dir: qemu_datadir / 'keymaps') - endif -endforeach + endforeach -if native_qemu_keymap.found() alias_target('update-keymaps', t) +else + install_data(keymaps.keys(), install_dir: qemu_datadir / 'keymaps') endif install_data(['sl', 'sv'], install_dir: qemu_datadir / 'keymaps') diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 41ba1c0ec7b..388e0db6e40 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -85,16 +85,9 @@ blobs = [ 'vof-nvram.bin', ] -ln_s = [find_program('ln', required: true), '-sf'] -foreach f : blobs - roms += custom_target(f, - build_by_default: have_system, - output: f, - input: files('meson.build'), # dummy input - install: get_option('install_blobs'), - install_dir: qemu_datadir, - command: [ ln_s, meson.project_source_root() / 'pc-bios' / f, '@OUTPUT@' ]) -endforeach +if get_option('install_blobs') + install_data(blobs, install_dir: qemu_datadir) +endif subdir('descriptors') subdir('keymaps') diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index f639915b4fe..e90ca2e1c68 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -11,7 +11,7 @@ CFLAGS = -O2 -g quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) cc-option = $(if $(shell $(CC) $1 -c -o /dev/null -xc /dev/null >/dev/null 2>&1 && echo OK), $1, $2) -override CFLAGS += -march=i486 -Wall +override CFLAGS += -march=i486 -Wall $(EXTRA_CFLAGS) -m16 # If -fcf-protection is enabled in flags or compiler defaults that will # conflict with -march=i486 @@ -24,21 +24,8 @@ override CFLAGS += $(filter -W%, $(QEMU_CFLAGS)) override CFLAGS += $(call cc-option, -fno-pie) override CFLAGS += -ffreestanding -I$(TOPSRC_DIR)/include override CFLAGS += $(call cc-option, -fno-stack-protector) -override CFLAGS += $(call cc-option, -m16) override CFLAGS += $(call cc-option, -Wno-array-bounds) -ifeq ($(filter -m16, $(CFLAGS)),) -# Attempt to work around compilers that lack -m16 (GCC <= 4.8, clang <= ??) -# On GCC we add -fno-toplevel-reorder to keep the order of asm blocks with -# respect to the rest of the code. clang does not have -fno-toplevel-reorder, -# but it places all asm blocks at the beginning and we're relying on it for -# the option ROM header. So just force clang not to use the integrated -# assembler, which doesn't support .code16gcc. -override CFLAGS += $(call cc-option, -fno-toplevel-reorder) -override CFLAGS += $(call cc-option, -no-integrated-as) -override CFLAGS += -m32 -include $(SRC_DIR)/code16gcc.h -endif - Wa = -Wa, override ASFLAGS += -32 override CFLAGS += $(call cc-option, $(Wa)-32) diff --git a/pc-bios/optionrom/code16gcc.h b/pc-bios/optionrom/code16gcc.h deleted file mode 100644 index 9c8d25d508a..00000000000 --- a/pc-bios/optionrom/code16gcc.h +++ /dev/null @@ -1,3 +0,0 @@ -asm( -".code16gcc\n" -); diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index a560c1272f6..39f9680a0ef 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 6eb713bf374..c8784c2a085 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -18,11 +18,11 @@ $(call set-vpath, $(SRC_PATH)) QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d %.o: %.c - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ + $(call quiet-command,$(CC) $(EXTRA_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ -c -o $@ $<,"CC","$(TARGET_DIR)$@") %.o: %.S - $(call quiet-command,$(CCAS) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ + $(call quiet-command,$(CCAS) $(EXTRA_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ -c -o $@ $<,"CCAS","$(TARGET_DIR)$@") .PHONY : all clean build-all @@ -30,14 +30,15 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o -QEMU_CFLAGS := -Wall $(filter -W%, $(QEMU_CFLAGS)) -QEMU_CFLAGS += $(call cc-option,-Werror $(QEMU_CFLAGS),-Wno-stringop-overflow) -QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE -QEMU_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables -QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector) -QEMU_CFLAGS += -msoft-float -QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS),-march=z900,-march=z10) -QEMU_CFLAGS += -std=gnu99 +EXTRA_CFLAGS := $(EXTRA_CFLAGS) -Wall +EXTRA_CFLAGS += $(call cc-option,-Werror $(EXTRA_CFLAGS),-Wno-stringop-overflow) +EXTRA_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE +EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables +EXTRA_CFLAGS += $(call cc-option, $(EXTRA_CFLAGS), -fno-stack-protector) +EXTRA_CFLAGS += $(call cc-option, $(EXTRA_CFLAGS), -Wno-array-bounds) +EXTRA_CFLAGS += -msoft-float +EXTRA_CFLAGS += $(call cc-option, $(EXTRA_CFLAGS),-march=z900,-march=z10) +EXTRA_CFLAGS += -std=gnu99 LDFLAGS += -Wl,-pie -nostdlib build-all: s390-ccw.img s390-netboot.img diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 56411ab3b67..994e59c0b02 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -780,18 +780,37 @@ static void ipl_iso_el_torito(void) } } +/** + * Detect whether we're trying to boot from an .ISO image. + * These always have a signature string "CD001" at offset 0x8001. + */ +static bool has_iso_signature(void) +{ + int blksize = virtio_get_block_size(); + + if (!blksize || virtio_read(0x8000 / blksize, sec)) { + return false; + } + + return !memcmp("CD001", &sec[1], 5); +} + /*********************************************************************** * Bus specific IPL sequences */ static void zipl_load_vblk(void) { - if (virtio_guessed_disk_nature()) { - virtio_assume_iso9660(); + int blksize = virtio_get_block_size(); + + if (blksize == VIRTIO_ISO_BLOCK_SIZE || has_iso_signature()) { + if (blksize != VIRTIO_ISO_BLOCK_SIZE) { + virtio_assume_iso9660(); + } + ipl_iso_el_torito(); } - ipl_iso_el_torito(); - if (virtio_guessed_disk_nature()) { + if (blksize != VIRTIO_DASD_DEFAULT_BLOCK_SIZE) { sclp_print("Using guessed DASD geometry.\n"); virtio_assume_eckd(); } diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 5d2b7ba94de..a2def83e823 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -14,6 +14,7 @@ #include "s390-ccw.h" #include "cio.h" #include "virtio.h" +#include "virtio-scsi.h" #include "dasd-ipl.h" char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); @@ -218,6 +219,7 @@ static int virtio_setup(void) { VDev *vdev = virtio_get_device(); QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; + int ret; memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); @@ -225,18 +227,26 @@ static int virtio_setup(void) menu_setup(); } - if (virtio_get_device_type() == VIRTIO_ID_NET) { + switch (vdev->senseid.cu_model) { + case VIRTIO_ID_NET: sclp_print("Network boot device detected\n"); vdev->netboot_start_addr = qipl.netboot_start_addr; - } else { - int ret = virtio_blk_setup_device(blk_schid); - if (ret) { - return ret; - } + return 0; + case VIRTIO_ID_BLOCK: + ret = virtio_blk_setup_device(blk_schid); + break; + case VIRTIO_ID_SCSI: + ret = virtio_scsi_setup_device(blk_schid); + break; + default: + panic("\n! No IPL device available !\n"); + } + + if (!ret) { IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); } - return 0; + return ret; } static void ipl_boot_device(void) @@ -281,7 +291,7 @@ static void probe_boot_device(void) sclp_print("Could not find a suitable boot device (none specified)\n"); } -int main(void) +void main(void) { sclp_setup(); css_setup(); @@ -294,5 +304,4 @@ int main(void) } panic("Failed to load OS from hard disk\n"); - return 0; /* make compiler happy */ } diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index 1a06befa4ba..7639da194c1 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -8,7 +8,7 @@ LIBNET_INC := -I$(SLOF_DIR)/lib/libnet NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x7800000 -$(NETOBJS): QEMU_CFLAGS += $(LIBC_INC) $(LIBNET_INC) +$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) s390-netboot.elf: $(NETOBJS) libnet.a libc.a $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,"BUILD","$(TARGET_DIR)$@") @@ -16,9 +16,12 @@ s390-netboot.elf: $(NETOBJS) libnet.a libc.a s390-netboot.img: s390-netboot.elf $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,"STRIP","$(TARGET_DIR)$@") +# SLOF is GCC-only, so ignore warnings about GNU extensions with Clang here +NO_GNU_WARN := $(call cc-option,-Werror $(QEMU_CFLAGS),-Wno-gnu) + # libc files: -LIBC_CFLAGS = $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ +LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(NO_GNU_WARN) $(LIBC_INC) $(LIBNET_INC) \ -MMD -MP -MT $@ -MF $(@:%.o=%.d) CTYPE_OBJS = isdigit.o isxdigit.o toupper.o @@ -52,7 +55,7 @@ libc.a: $(LIBCOBJS) LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o -LIBNETCFLAGS = $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ +LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(NO_GNU_WARN) $(LIBC_INC) $(LIBNET_INC) \ -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d) %.o : $(SLOF_DIR)/lib/libnet/%.c diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 79db69ff542..b88e0550ab9 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -57,6 +57,7 @@ void write_subsystem_identification(void); void write_iplb_location(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); unsigned int get_loadparm_index(void); +void main(void); /* sclp.c */ void sclp_print(const char *string); diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c index 7d35050292d..8271c472968 100644 --- a/pc-bios/s390-ccw/virtio-blkdev.c +++ b/pc-bios/s390-ccw/virtio-blkdev.c @@ -13,6 +13,9 @@ #include "virtio.h" #include "virtio-scsi.h" +#define VIRTIO_BLK_F_GEOMETRY (1 << 4) +#define VIRTIO_BLK_F_BLK_SIZE (1 << 6) + static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr, int sec_num) { @@ -112,23 +115,6 @@ VirtioGDN virtio_guessed_disk_nature(void) return virtio_get_device()->guessed_disk_nature; } -void virtio_assume_scsi(void) -{ - VDev *vdev = virtio_get_device(); - - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - vdev->guessed_disk_nature = VIRTIO_GDN_SCSI; - vdev->config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; - vdev->config.blk.physical_block_exp = 0; - vdev->blk_factor = 1; - break; - case VIRTIO_ID_SCSI: - vdev->scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; - break; - } -} - void virtio_assume_iso9660(void) { VDev *vdev = virtio_get_device(); @@ -155,7 +141,7 @@ void virtio_assume_eckd(void) vdev->config.blk.physical_block_exp = 0; switch (vdev->senseid.cu_model) { case VIRTIO_ID_BLOCK: - vdev->config.blk.blk_size = 4096; + vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE; break; case VIRTIO_ID_SCSI: vdev->config.blk.blk_size = vdev->scsi_block_size; @@ -166,46 +152,19 @@ void virtio_assume_eckd(void) virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size); } -bool virtio_disk_is_scsi(void) -{ - VDev *vdev = virtio_get_device(); - - if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI) { - return true; - } - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return (vdev->config.blk.geometry.heads == 255) - && (vdev->config.blk.geometry.sectors == 63) - && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); - case VIRTIO_ID_SCSI: - return true; - } - return false; -} - -bool virtio_disk_is_eckd(void) +bool virtio_ipl_disk_is_valid(void) { + int blksize = virtio_get_block_size(); VDev *vdev = virtio_get_device(); - const int block_size = virtio_get_block_size(); - if (vdev->guessed_disk_nature == VIRTIO_GDN_DASD) { + if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI || + vdev->guessed_disk_nature == VIRTIO_GDN_DASD) { return true; } - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return (vdev->config.blk.geometry.heads == 15) - && (vdev->config.blk.geometry.sectors == - virtio_eckd_sectors_for_block_size(block_size)); - case VIRTIO_ID_SCSI: - return false; - } - return false; -} -bool virtio_ipl_disk_is_valid(void) -{ - return virtio_disk_is_scsi() || virtio_disk_is_eckd(); + return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK || + vdev->senseid.cu_model == VIRTIO_ID_SCSI) && + blksize >= 512 && blksize <= 4096; } int virtio_get_block_size(void) @@ -266,34 +225,12 @@ uint64_t virtio_get_blocks(void) int virtio_blk_setup_device(SubChannelId schid) { VDev *vdev = virtio_get_device(); - int ret = 0; + vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE; vdev->schid = schid; virtio_setup_ccw(vdev); - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - sclp_print("Using virtio-blk.\n"); - if (!virtio_ipl_disk_is_valid()) { - /* make sure all getters but blocksize return 0 for - * invalid IPL disk - */ - memset(&vdev->config.blk, 0, sizeof(vdev->config.blk)); - virtio_assume_scsi(); - } - break; - case VIRTIO_ID_SCSI: - IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, - "Config: sense size mismatch"); - IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, - "Config: CDB size mismatch"); - - sclp_print("Using virtio-scsi.\n"); - ret = virtio_scsi_setup(vdev); - break; - default: - panic("\n! No IPL device available !\n"); - } + sclp_print("Using virtio-blk.\n"); - return ret; + return 0; } diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index 2c8d0f30970..3b7069270ce 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -329,7 +329,7 @@ static void scsi_parse_capacity_report(void *data, } } -int virtio_scsi_setup(VDev *vdev) +static int virtio_scsi_setup(VDev *vdev) { int retry_test_unit_ready = 3; uint8_t data[256]; @@ -430,3 +430,20 @@ int virtio_scsi_setup(VDev *vdev) return 0; } + +int virtio_scsi_setup_device(SubChannelId schid) +{ + VDev *vdev = virtio_get_device(); + + vdev->schid = schid; + virtio_setup_ccw(vdev); + + IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, + "Config: sense size mismatch"); + IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, + "Config: CDB size mismatch"); + + sclp_print("Using virtio-scsi.\n"); + + return virtio_scsi_setup(vdev); +} diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h index 4b14c2c2f90..e6b6cd4815b 100644 --- a/pc-bios/s390-ccw/virtio-scsi.h +++ b/pc-bios/s390-ccw/virtio-scsi.h @@ -67,8 +67,8 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r) return r->response == VIRTIO_SCSI_S_OK && r->status == CDB_STATUS_GOOD; } -int virtio_scsi_setup(VDev *vdev); int virtio_scsi_read_many(VDev *vdev, ulong sector, void *load_addr, int sec_num); +int virtio_scsi_setup_device(SubChannelId schid); #endif /* VIRTIO_SCSI_H */ diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 5d2c6e33816..f37510f3124 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -220,7 +220,7 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) void virtio_setup_ccw(VDev *vdev) { int i, rc, cfg_size = 0; - unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK; + uint8_t status; struct VirtioFeatureDesc { uint32_t features; uint8_t index; @@ -234,6 +234,10 @@ void virtio_setup_ccw(VDev *vdev) run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); + status = VIRTIO_CONFIG_S_ACKNOWLEDGE; + rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); + IPL_assert(rc == 0, "Could not write ACKNOWLEDGE status to host"); + switch (vdev->senseid.cu_model) { case VIRTIO_ID_NET: vdev->nr_vqs = 2; @@ -253,9 +257,10 @@ void virtio_setup_ccw(VDev *vdev) default: panic("Unsupported virtio device\n"); } - IPL_assert( - run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false) == 0, - "Could not get block device configuration"); + + status |= VIRTIO_CONFIG_S_DRIVER; + rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); + IPL_assert(rc == 0, "Could not write DRIVER status to host"); /* Feature negotiation */ for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) { @@ -269,6 +274,9 @@ void virtio_setup_ccw(VDev *vdev) IPL_assert(rc == 0, "Could not set features bits"); } + rc = run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false); + IPL_assert(rc == 0, "Could not get virtio device configuration"); + for (i = 0; i < vdev->nr_vqs; i++) { VqInfo info = { .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE), @@ -281,9 +289,8 @@ void virtio_setup_ccw(VDev *vdev) .num = 0, }; - IPL_assert( - run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false) == 0, - "Could not get block device VQ configuration"); + rc = run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false); + IPL_assert(rc == 0, "Could not get virtio device VQ configuration"); info.num = config.num; vring_init(&vdev->vrings[i], &info); vdev->vrings[i].schid = vdev->schid; @@ -291,9 +298,10 @@ void virtio_setup_ccw(VDev *vdev) run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0, "Cannot set VQ info"); } - IPL_assert( - run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false) == 0, - "Could not write status to host"); + + status |= VIRTIO_CONFIG_S_DRIVER_OK; + rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); + IPL_assert(rc == 0, "Could not write DRIVER_OK status to host"); } bool virtio_is_supported(SubChannelId schid) diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 19fceb6495f..e657d381eca 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -182,22 +182,20 @@ enum guessed_disk_nature_type { typedef enum guessed_disk_nature_type VirtioGDN; VirtioGDN virtio_guessed_disk_nature(void); -void virtio_assume_scsi(void); void virtio_assume_eckd(void); void virtio_assume_iso9660(void); -extern bool virtio_disk_is_scsi(void); -extern bool virtio_disk_is_eckd(void); -extern bool virtio_ipl_disk_is_valid(void); -extern int virtio_get_block_size(void); -extern uint8_t virtio_get_heads(void); -extern uint8_t virtio_get_sectors(void); -extern uint64_t virtio_get_blocks(void); -extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); +bool virtio_ipl_disk_is_valid(void); +int virtio_get_block_size(void); +uint8_t virtio_get_heads(void); +uint8_t virtio_get_sectors(void); +uint64_t virtio_get_blocks(void); +int virtio_read_many(ulong sector, void *load_addr, int sec_num); #define VIRTIO_SECTOR_SIZE 512 #define VIRTIO_ISO_BLOCK_SIZE 2048 #define VIRTIO_SCSI_BLOCK_SIZE 512 +#define VIRTIO_DASD_DEFAULT_BLOCK_SIZE 4096 static inline ulong virtio_sector_adjust(ulong sector) { diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img index bc34af8a287..682da24a05d 100644 Binary files a/pc-bios/s390-netboot.img and b/pc-bios/s390-netboot.img differ diff --git a/pc-bios/vof/Makefile b/pc-bios/vof/Makefile index 391ac0d6006..8809c827684 100644 --- a/pc-bios/vof/Makefile +++ b/pc-bios/vof/Makefile @@ -2,15 +2,13 @@ include config.mak VPATH=$(SRC_DIR) all: vof.bin -CC ?= $(CROSS)gcc -LD ?= $(CROSS)ld -OBJCOPY ?= $(CROSS)objcopy +EXTRA_CFLAGS += -mcpu=power4 %.o: %.S - $(CC) -m32 -mbig-endian -mcpu=power4 -c -o $@ $< + $(CC) $(EXTRA_CFLAGS) -c -o $@ $< %.o: %.c - $(CC) -m32 -mbig-endian -mcpu=power4 -c -fno-stack-protector -o $@ $< + $(CC) $(EXTRA_CFLAGS) -c -fno-stack-protector -o $@ $< vof.elf: entry.o main.o ci.o bootmem.o libc.o $(LD) -nostdlib -e_start -T$(SRC_DIR)/vof.lds -EB -o $@ $^ diff --git a/po/LINGUAS b/po/LINGUAS index cc4b5c3b36c..9b33a3659fc 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -5,4 +5,5 @@ hu it sv tr +uk zh_CN diff --git a/po/uk.po b/po/uk.po new file mode 100644 index 00000000000..ff037808bf1 --- /dev/null +++ b/po/uk.po @@ -0,0 +1,75 @@ +# Ukrainian translation for QEMU. +# This file is put in the public domain. +# Andrij Mizyk , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: QEMU 1.4.50\n" +"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" +"PO-Revision-Date: 2022-06-13 01:33+0300\n" +"Last-Translator: Andrij Mizyk \n" +"Language-Team: Ukrainian\n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Gtranslator 2.91.6\n" + +msgid " - Press Ctrl+Alt+G to release grab" +msgstr " - Натисніть Ctrl+Alt+G, щоб відпустити захоплення" + +msgid " [Paused]" +msgstr " [Призупинено]" + +msgid "_Pause" +msgstr "_Призупинити" + +msgid "_Reset" +msgstr "_Скинути" + +msgid "Power _Down" +msgstr "Вимкнути _живлення" + +msgid "_Quit" +msgstr "_Вийти" + +msgid "_Fullscreen" +msgstr "Повний _екран" + +msgid "_Copy" +msgstr "_Копіювати" + +msgid "Zoom _In" +msgstr "_Збільшити" + +msgid "Zoom _Out" +msgstr "З_меншити" + +msgid "Best _Fit" +msgstr "Найкращий _розмір" + +msgid "Zoom To _Fit" +msgstr "Збільшити до _розміру" + +msgid "Grab On _Hover" +msgstr "Захопити при _наведенні" + +msgid "_Grab Input" +msgstr "Захопити _введення" + +msgid "Show _Tabs" +msgstr "Показувати _вкладки" + +msgid "Detach Tab" +msgstr "Відʼєднати вкладку" + +msgid "Show Menubar" +msgstr "Показувати рядок меню" + +msgid "_Machine" +msgstr "_Машина" + +msgid "_View" +msgstr "_Вигляд" diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 03b5574618f..1951754455a 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -50,7 +50,7 @@ # QMPMessage can be outgoing commands or incoming events/returns. # QMPReturnValue is usually a dict/json object, but due to QAPI's -# 'returns-whitelist', it can actually be anything. +# 'command-returns-exceptions', it can actually be anything. # # {'return': {}} is a QMPMessage, # {} is the QMPReturnValue. diff --git a/qapi/block-core.json b/qapi/block-core.json index f0383c79255..2173e7734a4 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -737,7 +737,8 @@ # } # ## -{ 'command': 'query-block', 'returns': ['BlockInfo'] } +{ 'command': 'query-block', 'returns': ['BlockInfo'], + 'allow-preconfig': true } ## # @BlockDeviceTimedStats: @@ -1113,7 +1114,8 @@ ## { 'command': 'query-blockstats', 'data': { '*query-nodes': 'bool' }, - 'returns': ['BlockStats'] } + 'returns': ['BlockStats'], + 'allow-preconfig': true } ## # @BlockdevOnError: @@ -1262,7 +1264,8 @@ # # Since: 1.1 ## -{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] } +{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'], + 'allow-preconfig': true } ## # @block_resize: @@ -1293,7 +1296,8 @@ 'data': { '*device': 'str', '*node-name': 'str', 'size': 'int' }, - 'coroutine': true } + 'coroutine': true, + 'allow-preconfig': true } ## # @NewImageMode: @@ -1509,7 +1513,8 @@ # ## { 'command': 'blockdev-snapshot-sync', - 'data': 'BlockdevSnapshotSync' } + 'data': 'BlockdevSnapshotSync', + 'allow-preconfig': true } ## # @blockdev-snapshot: @@ -1550,7 +1555,8 @@ ## { 'command': 'blockdev-snapshot', 'data': 'BlockdevSnapshot', - 'features': [ 'allow-write-only-overlay' ] } + 'features': [ 'allow-write-only-overlay' ], + 'allow-preconfig': true } ## # @change-backing-file: @@ -1582,7 +1588,8 @@ ## { 'command': 'change-backing-file', 'data': { 'device': 'str', 'image-node-name': 'str', - 'backing-file': 'str' } } + 'backing-file': 'str' }, + 'allow-preconfig': true } ## # @block-commit: @@ -1692,7 +1699,8 @@ '*backing-file': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @drive-backup: @@ -1721,7 +1729,8 @@ # ## { 'command': 'drive-backup', 'boxed': true, - 'data': 'DriveBackup', 'features': ['deprecated'] } + 'data': 'DriveBackup', 'features': ['deprecated'], + 'allow-preconfig': true } ## # @blockdev-backup: @@ -1747,7 +1756,8 @@ # ## { 'command': 'blockdev-backup', 'boxed': true, - 'data': 'BlockdevBackup' } + 'data': 'BlockdevBackup', + 'allow-preconfig': true } ## # @query-named-block-nodes: @@ -1813,7 +1823,8 @@ ## { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ], - 'data': { '*flat': 'bool' } } + 'data': { '*flat': 'bool' }, + 'allow-preconfig': true } ## # @XDbgBlockGraphNodeType: @@ -1922,7 +1933,8 @@ # Since: 4.0 ## { 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @drive-mirror: @@ -1950,7 +1962,8 @@ # ## { 'command': 'drive-mirror', 'boxed': true, - 'data': 'DriveMirror' } + 'data': 'DriveMirror', + 'allow-preconfig': true } ## # @DriveMirror: @@ -2123,7 +2136,8 @@ # ## { 'command': 'block-dirty-bitmap-add', - 'data': 'BlockDirtyBitmapAdd' } + 'data': 'BlockDirtyBitmapAdd', + 'allow-preconfig': true } ## # @block-dirty-bitmap-remove: @@ -2147,7 +2161,8 @@ # ## { 'command': 'block-dirty-bitmap-remove', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-clear: @@ -2170,7 +2185,8 @@ # ## { 'command': 'block-dirty-bitmap-clear', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-enable: @@ -2191,7 +2207,8 @@ # ## { 'command': 'block-dirty-bitmap-enable', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-disable: @@ -2212,7 +2229,8 @@ # ## { 'command': 'block-dirty-bitmap-disable', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-merge: @@ -2244,7 +2262,8 @@ # ## { 'command': 'block-dirty-bitmap-merge', - 'data': 'BlockDirtyBitmapMerge' } + 'data': 'BlockDirtyBitmapMerge', + 'allow-preconfig': true } ## # @BlockDirtyBitmapSha256: @@ -2275,7 +2294,8 @@ ## { 'command': 'x-debug-block-dirty-bitmap-sha256', 'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @blockdev-mirror: @@ -2361,7 +2381,8 @@ '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', '*copy-mode': 'MirrorCopyMode', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @BlockIOThrottle: @@ -2663,7 +2684,8 @@ '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @block-job-set-speed: @@ -2687,7 +2709,8 @@ # Since: 1.1 ## { 'command': 'block-job-set-speed', - 'data': { 'device': 'str', 'speed': 'int' } } + 'data': { 'device': 'str', 'speed': 'int' }, + 'allow-preconfig': true } ## # @block-job-cancel: @@ -2726,7 +2749,8 @@ # # Since: 1.1 ## -{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' } } +{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' }, + 'allow-preconfig': true } ## # @block-job-pause: @@ -2750,7 +2774,8 @@ # # Since: 1.3 ## -{ 'command': 'block-job-pause', 'data': { 'device': 'str' } } +{ 'command': 'block-job-pause', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-resume: @@ -2772,7 +2797,8 @@ # # Since: 1.3 ## -{ 'command': 'block-job-resume', 'data': { 'device': 'str' } } +{ 'command': 'block-job-resume', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-complete: @@ -2800,7 +2826,8 @@ # # Since: 1.3 ## -{ 'command': 'block-job-complete', 'data': { 'device': 'str' } } +{ 'command': 'block-job-complete', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-dismiss: @@ -2820,7 +2847,8 @@ # # Since: 2.12 ## -{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } } +{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' }, + 'allow-preconfig': true } ## # @block-job-finalize: @@ -2838,7 +2866,8 @@ # # Since: 2.12 ## -{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } } +{ 'command': 'block-job-finalize', 'data': { 'id': 'str' }, + 'allow-preconfig': true } ## # @BlockdevDiscardOptions: @@ -4155,6 +4184,25 @@ 'base': 'BlockdevOptionsGenericFormat', 'data': { '*bottom': 'str' } } +## +# @OnCbwError: +# +# An enumeration of possible behaviors for copy-before-write operation +# failures. +# +# @break-guest-write: report the error to the guest. This way, the guest +# will not be able to overwrite areas that cannot be +# backed up, so the backup process remains valid. +# +# @break-snapshot: continue guest write. Doing so will make the provided +# snapshot state invalid and any backup or export +# process based on it will finally fail. +# +# Since: 7.1 +## +{ 'enum': 'OnCbwError', + 'data': [ 'break-guest-write', 'break-snapshot' ] } + ## # @BlockdevOptionsCbw: # @@ -4176,11 +4224,21 @@ # modifications (or removing) of specified bitmap doesn't # influence the filter. (Since 7.0) # +# @on-cbw-error: Behavior on failure of copy-before-write operation. +# Default is @break-guest-write. (Since 7.1) +# +# @cbw-timeout: Zero means no limit. Non-zero sets the timeout in seconds +# for copy-before-write operation. When a timeout occurs, +# the respective copy-before-write operation will fail, and +# the @on-cbw-error parameter will decide how this failure +# is handled. Default 0. (Since 7.1) +# # Since: 6.2 ## { 'struct': 'BlockdevOptionsCbw', 'base': 'BlockdevOptionsGenericFormat', - 'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap' } } + 'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap', + '*on-cbw-error': 'OnCbwError', '*cbw-timeout': 'uint32' } } ## # @BlockdevOptions: @@ -4354,7 +4412,8 @@ # <- { "return": {} } # ## -{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } +{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true, + 'allow-preconfig': true } ## # @blockdev-reopen: @@ -4398,7 +4457,8 @@ # Since: 6.1 ## { 'command': 'blockdev-reopen', - 'data': { 'options': ['BlockdevOptions'] } } + 'data': { 'options': ['BlockdevOptions'] }, + 'allow-preconfig': true } ## # @blockdev-del: @@ -4431,7 +4491,8 @@ # <- { "return": {} } # ## -{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } } +{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' }, + 'allow-preconfig': true } ## # @BlockdevCreateOptionsFile: @@ -4872,7 +4933,8 @@ ## { 'command': 'blockdev-create', 'data': { 'job-id': 'str', - 'options': 'BlockdevCreateOptions' } } + 'options': 'BlockdevCreateOptions' }, + 'allow-preconfig': true } ## # @BlockdevAmendOptionsLUKS: @@ -4944,7 +5006,8 @@ 'node-name': 'str', 'options': 'BlockdevAmendOptions', '*force': 'bool' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @BlockErrorAction: @@ -5294,7 +5357,8 @@ # ## { 'command': 'block-set-write-threshold', - 'data': { 'node-name': 'str', 'write-threshold': 'uint64' } } + 'data': { 'node-name': 'str', 'write-threshold': 'uint64' }, + 'allow-preconfig': true } ## # @x-blockdev-change: @@ -5355,7 +5419,8 @@ 'data' : { 'parent': 'str', '*child': 'str', '*node': 'str' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @x-blockdev-set-iothread: @@ -5397,7 +5462,8 @@ 'data' : { 'node-name': 'str', 'iothread': 'StrOrNull', '*force': 'bool' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @QuorumOpType: @@ -5529,7 +5595,8 @@ # ## { 'command': 'blockdev-snapshot-internal-sync', - 'data': 'BlockdevSnapshotInternal' } + 'data': 'BlockdevSnapshotInternal', + 'allow-preconfig': true } ## # @blockdev-snapshot-delete-internal-sync: @@ -5576,4 +5643,5 @@ ## { 'command': 'blockdev-snapshot-delete-internal-sync', 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, - 'returns': 'SnapshotInfo' } + 'returns': 'SnapshotInfo', + 'allow-preconfig': true } diff --git a/qapi/block-export.json b/qapi/block-export.json index 0685cb8b9ac..4627bbc4e60 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -65,7 +65,8 @@ 'data': { 'addr': 'SocketAddressLegacy', '*tls-creds': 'str', '*tls-authz': 'str', - '*max-connections': 'uint32' } } + '*max-connections': 'uint32' }, + 'allow-preconfig': true } ## # @BlockExportOptionsNbdBase: @@ -177,6 +178,27 @@ '*allow-other': 'FuseExportAllowOther' }, 'if': 'CONFIG_FUSE' } +## +# @BlockExportOptionsVduseBlk: +# +# A vduse-blk block export. +# +# @name: the name of VDUSE device (must be unique across the host). +# @num-queues: the number of virtqueues. Defaults to 1. +# @queue-size: the size of virtqueue. Defaults to 256. +# @logical-block-size: Logical block size in bytes. Range [512, PAGE_SIZE] +# and must be power of 2. Defaults to 512 bytes. +# @serial: the serial number of virtio block device. Defaults to empty string. +# +# Since: 7.1 +## +{ 'struct': 'BlockExportOptionsVduseBlk', + 'data': { 'name': 'str', + '*num-queues': 'uint16', + '*queue-size': 'uint16', + '*logical-block-size': 'size', + '*serial': 'str' } } + ## # @NbdServerAddOptions: # @@ -215,7 +237,8 @@ # Since: 1.3 ## { 'command': 'nbd-server-add', - 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'] } + 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'], + 'allow-preconfig': true } ## # @BlockExportRemoveMode: @@ -260,7 +283,8 @@ ## { 'command': 'nbd-server-remove', 'data': {'name': 'str', '*mode': 'BlockExportRemoveMode'}, - 'features': ['deprecated'] } + 'features': ['deprecated'], + 'allow-preconfig': true } ## # @nbd-server-stop: @@ -270,7 +294,8 @@ # # Since: 1.3 ## -{ 'command': 'nbd-server-stop' } +{ 'command': 'nbd-server-stop', + 'allow-preconfig': true } ## # @BlockExportType: @@ -280,6 +305,7 @@ # @nbd: NBD export # @vhost-user-blk: vhost-user-blk export (since 5.2) # @fuse: FUSE export (since: 6.0) +# @vduse-blk: vduse-blk export (since 7.1) # # Since: 4.2 ## @@ -287,7 +313,8 @@ 'data': [ 'nbd', { 'name': 'vhost-user-blk', 'if': 'CONFIG_VHOST_USER_BLK_SERVER' }, - { 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] } + { 'name': 'fuse', 'if': 'CONFIG_FUSE' }, + { 'name': 'vduse-blk', 'if': 'CONFIG_VDUSE_BLK_EXPORT' } ] } ## # @BlockExportOptions: @@ -331,7 +358,9 @@ 'vhost-user-blk': { 'type': 'BlockExportOptionsVhostUserBlk', 'if': 'CONFIG_VHOST_USER_BLK_SERVER' }, 'fuse': { 'type': 'BlockExportOptionsFuse', - 'if': 'CONFIG_FUSE' } + 'if': 'CONFIG_FUSE' }, + 'vduse-blk': { 'type': 'BlockExportOptionsVduseBlk', + 'if': 'CONFIG_VDUSE_BLK_EXPORT' } } } ## @@ -342,7 +371,8 @@ # Since: 5.2 ## { 'command': 'block-export-add', - 'data': 'BlockExportOptions', 'boxed': true } + 'data': 'BlockExportOptions', 'boxed': true, + 'allow-preconfig': true } ## # @block-export-del: @@ -362,7 +392,8 @@ # Since: 5.2 ## { 'command': 'block-export-del', - 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' } } + 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' }, + 'allow-preconfig': true } ## # @BLOCK_EXPORT_DELETED: @@ -406,4 +437,5 @@ # # Since: 5.2 ## -{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'] } +{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'], + 'allow-preconfig': true } diff --git a/qapi/block.json b/qapi/block.json index 19326641ac8..5fe068f903a 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -496,7 +496,8 @@ # <- { "return": {} } ## { 'command': 'block_set_io_throttle', 'boxed': true, - 'data': 'BlockIOThrottle' } + 'data': 'BlockIOThrottle', + 'allow-preconfig': true } ## # @block-latency-histogram-set: @@ -572,4 +573,5 @@ '*boundaries': ['uint64'], '*boundaries-read': ['uint64'], '*boundaries-write': ['uint64'], - '*boundaries-flush': ['uint64'] } } + '*boundaries-flush': ['uint64'] }, + 'allow-preconfig': true } diff --git a/qapi/meson.build b/qapi/meson.build index 656ef0e039f..fd5c93d643a 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -46,6 +46,7 @@ qapi_all_modules = [ 'replay', 'run-state', 'sockets', + 'stats', 'trace', 'transaction', 'yank', diff --git a/qapi/migration.json b/qapi/migration.json index 6130cd9fae3..81185d4311c 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -55,6 +55,10 @@ # @postcopy-bytes: The number of bytes sent during the post-copy phase # (since 7.0). # +# @dirty-sync-missed-zero-copy: Number of times dirty RAM synchronization could +# not avoid copying dirty pages. This is between +# 0 and @dirty-sync-count * @multifd-channels. +# (since 7.1) # Since: 0.14 ## { 'struct': 'MigrationStats', @@ -65,7 +69,8 @@ 'postcopy-requests' : 'int', 'page-size' : 'int', 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64', 'precopy-bytes' : 'uint64', 'downtime-bytes' : 'uint64', - 'postcopy-bytes' : 'uint64' } } + 'postcopy-bytes' : 'uint64', + 'dirty-sync-missed-zero-copy' : 'uint64' } } ## # @XBZRLECacheStats: @@ -461,6 +466,18 @@ # procedure starts. The VM RAM is saved with running VM. # (since 6.0) # +# @zero-copy-send: Controls behavior on sending memory pages on migration. +# When true, enables a zero-copy mechanism for sending +# memory pages, if host supports it. +# Requires that QEMU be permitted to use locked memory +# for guest RAM pages. +# (since 7.1) +# @postcopy-preempt: If enabled, the migration process will allow postcopy +# requests to preempt precopy stream, so postcopy requests +# will be handled faster. This is a performance feature and +# should not affect the correctness of postcopy migration. +# (since 7.1) +# # Features: # @unstable: Members @x-colo and @x-ignore-shared are experimental. # @@ -474,7 +491,8 @@ 'block', 'return-path', 'pause-before-switchover', 'multifd', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, - 'validate-uuid', 'background-snapshot'] } + 'validate-uuid', 'background-snapshot', + 'zero-copy-send', 'postcopy-preempt'] } ## # @MigrationCapabilityStatus: @@ -738,12 +756,6 @@ # will consume more CPU. # Defaults to 1. (Since 5.0) # -# @zero-copy-send: Controls behavior on sending memory pages on migration. -# When true, enables a zero-copy mechanism for sending -# memory pages, if host supports it. -# Requires that QEMU be permitted to use locked memory -# for guest RAM pages. -# Defaults to false. (Since 7.1) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to # aliases for the purpose of dirty bitmap migration. Such @@ -784,7 +796,6 @@ 'xbzrle-cache-size', 'max-postcopy-bandwidth', 'max-cpu-throttle', 'multifd-compression', 'multifd-zlib-level' ,'multifd-zstd-level', - { 'name': 'zero-copy-send', 'if' : 'CONFIG_LINUX'}, 'block-bitmap-mapping' ] } ## @@ -911,13 +922,6 @@ # will consume more CPU. # Defaults to 1. (Since 5.0) # -# @zero-copy-send: Controls behavior on sending memory pages on migration. -# When true, enables a zero-copy mechanism for sending -# memory pages, if host supports it. -# Requires that QEMU be permitted to use locked memory -# for guest RAM pages. -# Defaults to false. (Since 7.1) -# # @block-bitmap-mapping: Maps block nodes and bitmaps on them to # aliases for the purpose of dirty bitmap migration. Such # aliases may for example be the corresponding names on the @@ -972,7 +976,6 @@ '*multifd-compression': 'MultiFDCompression', '*multifd-zlib-level': 'uint8', '*multifd-zstd-level': 'uint8', - '*zero-copy-send': { 'type': 'bool', 'if': 'CONFIG_LINUX' }, '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ] } } ## @@ -1119,13 +1122,6 @@ # will consume more CPU. # Defaults to 1. (Since 5.0) # -# @zero-copy-send: Controls behavior on sending memory pages on migration. -# When true, enables a zero-copy mechanism for sending -# memory pages, if host supports it. -# Requires that QEMU be permitted to use locked memory -# for guest RAM pages. -# Defaults to false. (Since 7.1) -# # @block-bitmap-mapping: Maps block nodes and bitmaps on them to # aliases for the purpose of dirty bitmap migration. Such # aliases may for example be the corresponding names on the @@ -1178,7 +1174,6 @@ '*multifd-compression': 'MultiFDCompression', '*multifd-zlib-level': 'uint8', '*multifd-zstd-level': 'uint8', - '*zero-copy-send': { 'type': 'bool', 'if': 'CONFIG_LINUX' }, '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ] } } ## @@ -1883,6 +1878,86 @@ ## { 'command': 'query-dirty-rate', 'returns': 'DirtyRateInfo' } +## +# @DirtyLimitInfo: +# +# Dirty page rate limit information of a virtual CPU. +# +# @cpu-index: index of a virtual CPU. +# +# @limit-rate: upper limit of dirty page rate (MB/s) for a virtual +# CPU, 0 means unlimited. +# +# @current-rate: current dirty page rate (MB/s) for a virtual CPU. +# +# Since: 7.1 +# +## +{ 'struct': 'DirtyLimitInfo', + 'data': { 'cpu-index': 'int', + 'limit-rate': 'uint64', + 'current-rate': 'uint64' } } + +## +# @set-vcpu-dirty-limit: +# +# Set the upper limit of dirty page rate for virtual CPUs. +# +# Requires KVM with accelerator property "dirty-ring-size" set. +# A virtual CPU's dirty page rate is a measure of its memory load. +# To observe dirty page rates, use @calc-dirty-rate. +# +# @cpu-index: index of a virtual CPU, default is all. +# +# @dirty-rate: upper limit of dirty page rate (MB/s) for virtual CPUs. +# +# Since: 7.1 +# +# Example: +# {"execute": "set-vcpu-dirty-limit"} +# "arguments": { "dirty-rate": 200, +# "cpu-index": 1 } } +# +## +{ 'command': 'set-vcpu-dirty-limit', + 'data': { '*cpu-index': 'int', + 'dirty-rate': 'uint64' } } + +## +# @cancel-vcpu-dirty-limit: +# +# Cancel the upper limit of dirty page rate for virtual CPUs. +# +# Cancel the dirty page limit for the vCPU which has been set with +# set-vcpu-dirty-limit command. Note that this command requires +# support from dirty ring, same as the "set-vcpu-dirty-limit". +# +# @cpu-index: index of a virtual CPU, default is all. +# +# Since: 7.1 +# +# Example: +# {"execute": "cancel-vcpu-dirty-limit"} +# "arguments": { "cpu-index": 1 } } +# +## +{ 'command': 'cancel-vcpu-dirty-limit', + 'data': { '*cpu-index': 'int'} } + +## +# @query-vcpu-dirty-limit: +# +# Returns information about virtual CPU dirty page rate limits, if any. +# +# Since: 7.1 +# +# Example: +# {"execute": "query-vcpu-dirty-limit"} +# +## +{ 'command': 'query-vcpu-dirty-limit', + 'returns': [ 'DirtyLimitInfo' ] } + ## # @snapshot-save: # diff --git a/qapi/net.json b/qapi/net.json index d6f7cfd4d65..75ba2cb9890 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -298,7 +298,7 @@ # # @udp: use the udp version of l2tpv3 encapsulation # -# @cookie64: use 64 bit coookies +# @cookie64: use 64 bit cookies # # @counter: have sequence counter # @@ -445,12 +445,19 @@ # @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) +# +# Features: +# @unstable: Member @x-svq is experimental. +# # Since: 5.1 ## { 'struct': 'NetdevVhostVDPAOptions', 'data': { '*vhostdev': 'str', - '*queues': 'int' } } + '*queues': 'int', + '*x-svq': {'type': 'bool', 'features' : [ 'unstable'] } } } ## # @NetdevVmnetHostOptions: diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 4912b9744e6..92d7ecc52c5 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -93,3 +93,4 @@ { 'include': 'audio.json' } { 'include': 'acpi.json' } { 'include': 'pci.json' } +{ 'include': 'stats.json' } diff --git a/qapi/stats.json b/qapi/stats.json new file mode 100644 index 00000000000..57db5b1c741 --- /dev/null +++ b/qapi/stats.json @@ -0,0 +1,251 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# = Statistics +## + +## +# @StatsType: +# +# Enumeration of statistics types +# +# @cumulative: stat is cumulative; value can only increase. +# @instant: stat is instantaneous; value can increase or decrease. +# @peak: stat is the peak value; value can only increase. +# @linear-histogram: stat is a linear histogram. +# @log2-histogram: stat is a logarithmic histogram, with one bucket +# for each power of two. +# +# Since: 7.1 +## +{ 'enum' : 'StatsType', + 'data' : [ 'cumulative', 'instant', 'peak', 'linear-histogram', + 'log2-histogram' ] } + +## +# @StatsUnit: +# +# Enumeration of unit of measurement for statistics +# +# @bytes: stat reported in bytes. +# @seconds: stat reported in seconds. +# @cycles: stat reported in clock cycles. +# @boolean: stat is a boolean value. +# +# Since: 7.1 +## +{ 'enum' : 'StatsUnit', + 'data' : [ 'bytes', 'seconds', 'cycles', 'boolean' ] } + +## +# @StatsProvider: +# +# Enumeration of statistics providers. +# +# Since: 7.1 +## +{ 'enum': 'StatsProvider', + 'data': [ 'kvm' ] } + +## +# @StatsTarget: +# +# The kinds of objects on which one can request statistics. +# +# @vm: statistics that apply to the entire virtual machine or +# the entire QEMU process. +# +# @vcpu: statistics that apply to a single virtual CPU. +# +# Since: 7.1 +## +{ 'enum': 'StatsTarget', + 'data': [ 'vm', 'vcpu' ] } + +## +# @StatsRequest: +# +# Indicates a set of statistics that should be returned by query-stats. +# +# @provider: provider for which to return statistics. + +# @names: statistics to be returned (all if omitted). +# +# Since: 7.1 +## +{ 'struct': 'StatsRequest', + 'data': { 'provider': 'StatsProvider', + '*names': [ 'str' ] } } + +## +# @StatsVCPUFilter: +# +# @vcpus: list of QOM paths for the desired vCPU objects. +# +# Since: 7.1 +## +{ 'struct': 'StatsVCPUFilter', + 'data': { '*vcpus': [ 'str' ] } } + +## +# @StatsFilter: +# +# The arguments to the query-stats command; specifies a target for which to +# request statistics and optionally the required subset of information for +# that target: +# - which vCPUs to request statistics for +# - which providers to request statistics from +# - which named values to return within each provider +# +# Since: 7.1 +## +{ 'union': 'StatsFilter', + 'base': { + 'target': 'StatsTarget', + '*providers': [ 'StatsRequest' ] }, + 'discriminator': 'target', + 'data': { 'vcpu': 'StatsVCPUFilter' } } + +## +# @StatsValue: +# +# @scalar: single unsigned 64-bit integers. +# @list: list of unsigned 64-bit integers (used for histograms). +# +# Since: 7.1 +## +{ 'alternate': 'StatsValue', + 'data': { 'scalar': 'uint64', + 'boolean': 'bool', + 'list': [ 'uint64' ] } } + +## +# @Stats: +# +# @name: name of stat. +# @value: stat value. +# +# Since: 7.1 +## +{ 'struct': 'Stats', + 'data': { 'name': 'str', + 'value' : 'StatsValue' } } + +## +# @StatsResult: +# +# @provider: provider for this set of statistics. +# +# @qom-path: Path to the object for which the statistics are returned, +# if the object is exposed in the QOM tree +# +# @stats: list of statistics. +# +# Since: 7.1 +## +{ 'struct': 'StatsResult', + 'data': { 'provider': 'StatsProvider', + '*qom-path': 'str', + 'stats': [ 'Stats' ] } } + +## +# @query-stats: +# +# Return runtime-collected statistics for objects such as the +# VM or its vCPUs. +# +# The arguments are a StatsFilter and specify the provider and objects +# to return statistics about. +# +# Returns: a list of StatsResult, one for each provider and object +# (e.g., for each vCPU). +# +# Since: 7.1 +## +{ 'command': 'query-stats', + 'data': 'StatsFilter', + 'boxed': true, + 'returns': [ 'StatsResult' ] } + +## +# @StatsSchemaValue: +# +# Schema for a single statistic. +# +# @name: name of the statistic; each element of the schema is uniquely +# identified by a target, a provider (both available in @StatsSchema) +# and the name. +# +# @type: kind of statistic. +# +# @unit: basic unit of measure for the statistic; if missing, the statistic +# is a simple number or counter. +# +# @base: base for the multiple of @unit in which the statistic is measured. +# Only present if @exponent is non-zero; @base and @exponent together +# form a SI prefix (e.g., _nano-_ for ``base=10`` and ``exponent=-9``) +# or IEC binary prefix (e.g. _kibi-_ for ``base=2`` and ``exponent=10``) +# +# @exponent: exponent for the multiple of @unit in which the statistic is +# expressed, or 0 for the basic unit +# +# @bucket-size: Present when @type is "linear-histogram", contains the width +# of each bucket of the histogram. +# +# Since: 7.1 +## +{ 'struct': 'StatsSchemaValue', + 'data': { 'name': 'str', + 'type': 'StatsType', + '*unit': 'StatsUnit', + '*base': 'int8', + 'exponent': 'int16', + '*bucket-size': 'uint32' } } + +## +# @StatsSchema: +# +# Schema for all available statistics for a provider and target. +# +# @provider: provider for this set of statistics. +# +# @target: the kind of object that can be queried through the provider. +# +# @stats: list of statistics. +# +# Since: 7.1 +## +{ 'struct': 'StatsSchema', + 'data': { 'provider': 'StatsProvider', + 'target': 'StatsTarget', + 'stats': [ 'StatsSchemaValue' ] } } + +## +# @query-stats-schemas: +# +# Return the schema for all available runtime-collected statistics. +# +# Note: runtime-collected statistics and their names fall outside QEMU's usual +# deprecation policies. QEMU will try to keep the set of available data +# stable, together with their names, but will not guarantee stability +# at all costs; the same is true of providers that source statistics +# externally, e.g. from Linux. For example, if the same value is being +# tracked with different names on different architectures or by different +# providers, one of them might be renamed. A statistic might go away if +# an algorithm is changed or some code is removed; changing a default +# might cause previously useful statistics to always report 0. Such +# changes, however, are expected to be rare. +# +# Since: 7.1 +## +{ 'command': 'query-stats-schemas', + 'data': { '*provider': 'StatsProvider' }, + 'returns': [ 'StatsSchema' ] } diff --git a/qapi/ui.json b/qapi/ui.json index 413371d5e8b..cf58ab4283d 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1195,12 +1195,17 @@ # assuming the guest will resize the display to match # the window size then. Otherwise it defaults to "off". # Since 3.1 +# @show-tabs: Display the tab bar for switching between the various graphical +# interfaces (e.g. VGA and virtual console character devices) +# by default. +# Since 7.1 # # Since: 2.12 ## { 'struct' : 'DisplayGTK', 'data' : { '*grab-on-hover' : 'bool', - '*zoom-to-fit' : 'bool' } } + '*zoom-to-fit' : 'bool', + '*show-tabs' : 'bool' } } ## # @DisplayEGLHeadless: diff --git a/qemu-img.c b/qemu-img.c index 4cf4d2423df..7d4b33b3daa 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1309,7 +1309,7 @@ static int check_empty_sectors(BlockBackend *blk, int64_t offset, int ret = 0; int64_t idx; - ret = blk_pread(blk, offset, buffer, bytes); + ret = blk_pread(blk, offset, bytes, buffer, 0); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", offset, filename, strerror(-ret)); @@ -1526,7 +1526,7 @@ static int img_compare(int argc, char **argv) int64_t pnum; chunk = MIN(chunk, IO_BUF_SIZE); - ret = blk_pread(blk1, offset, buf1, chunk); + ret = blk_pread(blk1, offset, chunk, buf1, 0); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", @@ -1534,7 +1534,7 @@ static int img_compare(int argc, char **argv) ret = 4; goto out; } - ret = blk_pread(blk2, offset, buf2, chunk); + ret = blk_pread(blk2, offset, chunk, buf2, 0); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", @@ -2114,7 +2114,7 @@ static int convert_do_copy(ImgConvertState *s) if (s->compressed && !s->ret) { /* signal EOF to align */ - ret = blk_pwrite_compressed(s->target, 0, NULL, 0); + ret = blk_pwrite_compressed(s->target, 0, 0, NULL); if (ret < 0) { return ret; } @@ -3779,7 +3779,7 @@ static int img_rebase(int argc, char **argv) n = old_backing_size - offset; } - ret = blk_pread(blk_old_backing, offset, buf_old, n); + ret = blk_pread(blk_old_backing, offset, n, buf_old, 0); if (ret < 0) { error_report("error while reading from old backing file"); goto out; @@ -3793,7 +3793,7 @@ static int img_rebase(int argc, char **argv) n = new_backing_size - offset; } - ret = blk_pread(blk_new_backing, offset, buf_new, n); + ret = blk_pread(blk_new_backing, offset, n, buf_new, 0); if (ret < 0) { error_report("error while reading from new backing file"); goto out; @@ -3812,8 +3812,8 @@ static int img_rebase(int argc, char **argv) if (buf_old_is_zero) { ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); } else { - ret = blk_pwrite(blk, offset + written, - buf_old + written, pnum, 0); + ret = blk_pwrite(blk, offset + written, pnum, + buf_old + written, 0); } if (ret < 0) { error_report("Error while writing to COW image: %s", @@ -5120,30 +5120,23 @@ static int img_dd(int argc, char **argv) in.buf = g_new(uint8_t, in.bsz); for (out_pos = 0; in_pos < size; block_count++) { - int in_ret, out_ret; + int bytes = (in_pos + in.bsz > size) ? size - in_pos : in.bsz; - if (in_pos + in.bsz > size) { - in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos); - } else { - in_ret = blk_pread(blk1, in_pos, in.buf, in.bsz); - } - if (in_ret < 0) { + ret = blk_pread(blk1, in_pos, bytes, in.buf, 0); + if (ret < 0) { error_report("error while reading from input image file: %s", - strerror(-in_ret)); - ret = -1; + strerror(-ret)); goto out; } - in_pos += in_ret; + in_pos += bytes; - out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0); - - if (out_ret < 0) { + ret = blk_pwrite(blk2, out_pos, bytes, in.buf, 0); + if (ret < 0) { error_report("error while writing to output image file: %s", - strerror(-out_ret)); - ret = -1; + strerror(-ret)); goto out; } - out_pos += out_ret; + out_pos += bytes; } out: diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 2f0d8ac25a4..952dc940f1d 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -541,28 +541,34 @@ create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov, static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int64_t bytes, int64_t *total) { + int ret; + if (bytes > INT_MAX) { return -ERANGE; } - *total = blk_pread(blk, offset, (uint8_t *)buf, bytes); - if (*total < 0) { - return *total; + ret = blk_pread(blk, offset, bytes, (uint8_t *)buf, 0); + if (ret < 0) { + return ret; } + *total = bytes; return 1; } static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int64_t bytes, int flags, int64_t *total) { + int ret; + if (bytes > INT_MAX) { return -ERANGE; } - *total = blk_pwrite(blk, offset, (uint8_t *)buf, bytes, flags); - if (*total < 0) { - return *total; + ret = blk_pwrite(blk, offset, bytes, (uint8_t *)buf, flags); + if (ret < 0) { + return ret; } + *total = bytes; return 1; } @@ -625,7 +631,7 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, return -ERANGE; } - ret = blk_pwrite_compressed(blk, offset, buf, bytes); + ret = blk_pwrite_compressed(blk, offset, bytes, buf); if (ret < 0) { return ret; } diff --git a/qemu-options.hx b/qemu-options.hx index 377d22fbd82..79e00916a11 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1938,7 +1938,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #endif #if defined(CONFIG_GTK) "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n" - " [,show-cursor=on|off][,window-close=on|off]\n" + " [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n" #endif #if defined(CONFIG_VNC) "-display vnc=[,]\n" @@ -2023,6 +2023,10 @@ SRST ``grab-on-hover=on|off`` : Grab keyboard input on mouse hover + ``show-tabs=on|off`` : Display the tab bar for switching between the + various graphical interfaces (e.g. VGA and + virtual console character devices) by default. + ``show-cursor=on|off`` : Force showing the mouse cursor ``window-close=on|off`` : Allow to quit qemu with window close button diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0469dc409d4..954efed01b2 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1207,7 +1207,15 @@ static void build_guest_fsinfo_for_device(char const *devpath, syspath = realpath(devpath, NULL); if (!syspath) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + if (errno != ENOENT) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return; + } + + /* ENOENT: This devpath may not exist because of container config */ + if (!fs->name) { + fs->name = g_path_get_basename(devpath); + } return; } @@ -2893,6 +2901,90 @@ GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) return guest_get_diskstats(errp); } +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + GuestCpuStatsList *head = NULL, **tail = &head; + const char *cpustats = "/proc/stat"; + int clk_tck = sysconf(_SC_CLK_TCK); + FILE *fp; + size_t n; + char *line = NULL; + + fp = fopen(cpustats, "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", cpustats); + return NULL; + } + + while (getline(&line, &n, fp) != -1) { + GuestCpuStats *cpustat = NULL; + GuestLinuxCpuStats *linuxcpustat; + int i; + unsigned long user, system, idle, iowait, irq, softirq, steal, guest; + unsigned long nice, guest_nice; + char name[64]; + + i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + name, &user, &nice, &system, &idle, &iowait, &irq, &softirq, + &steal, &guest, &guest_nice); + + /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */ + if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) { + continue; + } + + if (i < 5) { + slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats); + break; + } + + cpustat = g_new0(GuestCpuStats, 1); + cpustat->type = GUEST_CPU_STATS_TYPE_LINUX; + + linuxcpustat = &cpustat->u.q_linux; + linuxcpustat->cpu = atoi(&name[3]); + linuxcpustat->user = user * 1000 / clk_tck; + linuxcpustat->nice = nice * 1000 / clk_tck; + linuxcpustat->system = system * 1000 / clk_tck; + linuxcpustat->idle = idle * 1000 / clk_tck; + + if (i > 5) { + linuxcpustat->has_iowait = true; + linuxcpustat->iowait = iowait * 1000 / clk_tck; + } + + if (i > 6) { + linuxcpustat->has_irq = true; + linuxcpustat->irq = irq * 1000 / clk_tck; + linuxcpustat->has_softirq = true; + linuxcpustat->softirq = softirq * 1000 / clk_tck; + } + + if (i > 8) { + linuxcpustat->has_steal = true; + linuxcpustat->steal = steal * 1000 / clk_tck; + } + + if (i > 9) { + linuxcpustat->has_guest = true; + linuxcpustat->guest = guest * 1000 / clk_tck; + } + + if (i > 10) { + linuxcpustat->has_guest = true; + linuxcpustat->guest = guest * 1000 / clk_tck; + linuxcpustat->has_guestnice = true; + linuxcpustat->guestnice = guest_nice * 1000 / clk_tck; + } + + QAPI_LIST_APPEND(tail, cpustat); + } + + free(line); + fclose(fp); + return head; +} + #else /* defined(__linux__) */ void qmp_guest_suspend_disk(Error **errp) @@ -3247,6 +3339,11 @@ GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) return NULL; } +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} #endif /* CONFIG_FSFREEZE */ diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 36f94c0f9ca..7ed76647155 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -2543,3 +2543,9 @@ GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) error_setg(errp, QERR_UNSUPPORTED); return NULL; } + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} diff --git a/qga/main.c b/qga/main.c index c373fec3ee6..5f1efa23334 100644 --- a/qga/main.c +++ b/qga/main.c @@ -223,6 +223,10 @@ void reopen_fd_to_null(int fd) static void usage(const char *cmd) { +#ifdef CONFIG_FSFREEZE + g_autofree char *fsfreeze_hook = get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT); +#endif + printf( "Usage: %s [-m -p ] []\n" "QEMU Guest Agent " QEMU_FULL_VERSION "\n" @@ -270,7 +274,7 @@ QEMU_HELP_BOTTOM "\n" , cmd, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE - QGA_FSFREEZE_HOOK_DEFAULT, + fsfreeze_hook, #endif dfl_pathnames.state_dir); } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 9fa20e791b3..869399ea1a8 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1576,3 +1576,84 @@ { 'command': 'guest-get-diskstats', 'returns': ['GuestDiskStatsInfo'] } + +## +# @GuestCpuStatsType: +# +# An enumeration of OS type +# +# Since: 7.1 +## +{ 'enum': 'GuestCpuStatsType', + 'data': [ 'linux' ] } + + +## +# @GuestLinuxCpuStats: +# +# CPU statistics of Linux +# +# @cpu: CPU index in guest OS +# +# @user: Time spent in user mode +# +# @nice: Time spent in user mode with low priority (nice) +# +# @system: Time spent in system mode +# +# @idle: Time spent in the idle task +# +# @iowait: Time waiting for I/O to complete (since Linux 2.5.41) +# +# @irq: Time servicing interrupts (since Linux 2.6.0-test4) +# +# @softirq: Time servicing softirqs (since Linux 2.6.0-test4) +# +# @steal: Stolen time by host (since Linux 2.6.11) +# +# @guest: ime spent running a virtual CPU for guest operating systems under +# the control of the Linux kernel (since Linux 2.6.24) +# +# @guestnice: Time spent running a niced guest (since Linux 2.6.33) +# +# Since: 7.1 +## +{ 'struct': 'GuestLinuxCpuStats', + 'data': {'cpu': 'int', + 'user': 'uint64', + 'nice': 'uint64', + 'system': 'uint64', + 'idle': 'uint64', + '*iowait': 'uint64', + '*irq': 'uint64', + '*softirq': 'uint64', + '*steal': 'uint64', + '*guest': 'uint64', + '*guestnice': 'uint64' + } } + +## +# @GuestCpuStats: +# +# Get statistics of each CPU in millisecond. +# +# - @linux: Linux style CPU statistics +# +# Since: 7.1 +## +{ 'union': 'GuestCpuStats', + 'base': { 'type': 'GuestCpuStatsType' }, + 'discriminator': 'type', + 'data': { 'linux': 'GuestLinuxCpuStats' } } + +## +# @guest-get-cpustats: +# +# Retrieve information about CPU stats. +# Returns: List of CPU stats of guest. +# +# Since: 7.1 +## +{ 'command': 'guest-get-cpustats', + 'returns': ['GuestCpuStats'] +} diff --git a/scripts/clean-header-guards.pl b/scripts/clean-header-guards.pl index a6680253b1f..a7fd8dc99f4 100755 --- a/scripts/clean-header-guards.pl +++ b/scripts/clean-header-guards.pl @@ -32,8 +32,8 @@ use Getopt::Std; # Stuff we don't want to clean because we import it into our tree: -my $exclude = qr,^(disas/libvixl/|include/standard-headers/ - |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x; +my $exclude = qr,^(include/standard-headers/|linux-headers/ + |pc-bios/|tests/tcg/|tests/multiboot/),x; # Stuff that is expected to fail the preprocessing test: my $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,; diff --git a/scripts/clean-includes b/scripts/clean-includes index aaa7d4ceb38..d37bd4f6928 100755 --- a/scripts/clean-includes +++ b/scripts/clean-includes @@ -51,7 +51,7 @@ GIT=no DUPHEAD=no # Extended regular expression defining files to ignore when using --all -XDIRREGEX='^(tests/tcg|tests/multiboot|pc-bios|disas/libvixl)' +XDIRREGEX='^(tests/tcg|tests/multiboot|pc-bios)' while true do diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 183f26a32c9..de2eb962416 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -87,9 +87,6 @@ io ipmi ~ (/qemu)?((/include)?/hw/ipmi/.*) -libvixl - ~ (/qemu)?(/disas/libvixl/.*) - migration ~ (/qemu)?((/include)?/migration/.*) diff --git a/scripts/gensyscalls.sh b/scripts/gensyscalls.sh index 8fb450e3c96..a2f7664b7bb 100755 --- a/scripts/gensyscalls.sh +++ b/scripts/gensyscalls.sh @@ -44,6 +44,7 @@ read_includes() cpp -P -nostdinc -fdirectives-only \ -D_UAPI_ASM_$(upper ${arch})_BITSPERLONG_H \ + -D__ASM_$(upper ${arch})_BITSPERLONG_H \ -D__BITS_PER_LONG=${bits} \ -I${linux}/arch/${arch}/include/uapi/ \ -I${linux}/include/uapi \ @@ -99,4 +100,5 @@ generate_syscall_nr openrisc 32 "$output/linux-user/openrisc/syscall_nr.h" generate_syscall_nr riscv 32 "$output/linux-user/riscv/syscall32_nr.h" generate_syscall_nr riscv 64 "$output/linux-user/riscv/syscall64_nr.h" generate_syscall_nr hexagon 32 "$output/linux-user/hexagon/syscall_nr.h" +generate_syscall_nr loongarch 64 "$output/linux-user/loongarch64/syscall_nr.h" rm -fr "$TMP" diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py index e624c16b01a..3e2b4785388 100755 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -156,7 +156,7 @@ def cli_metavar(opt): if opt["type"] == "string": return "VALUE" if opt["type"] == "array": - return "CHOICES" + return "CHOICES" if "choices" in opt else "VALUES" return "CHOICE" @@ -199,7 +199,10 @@ def print_parse(options): key = cli_option(opt) name = opt["name"] if require_arg(opt): - print(f' --{key}=*) quote_sh "-D{name}=$2" ;;') + if opt["type"] == "array" and not "choices" in opt: + print(f' --{key}=*) quote_sh "-D{name}=$(meson_option_build_array $2)" ;;') + else: + print(f' --{key}=*) quote_sh "-D{name}=$2" ;;') elif opt["type"] == "boolean": print(f' --enable-{key}) printf "%s" -D{name}=true ;;') print(f' --disable-{key}) printf "%s" -D{name}=false ;;') diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 24eb5f35eae..f7abdeffee8 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -42,12 +42,12 @@ meson_options_help() { printf "%s\n" ' --enable-trace-backends=CHOICES' printf "%s\n" ' Set available tracing backends [log] (choices:' printf "%s\n" ' dtrace/ftrace/log/nop/simple/syslog/ust)' - printf "%s\n" ' --firmwarepath=VALUE search PATH for firmware files [qemu-firmware]' + printf "%s\n" ' --firmwarepath=VALUES search PATH for firmware files [share/qemu-firmware]' printf "%s\n" ' --iasl=VALUE Path to ACPI disassembler' printf "%s\n" ' --includedir=VALUE Header file directory [include]' printf "%s\n" ' --interp-prefix=VALUE where to find shared libraries etc., use %M for' printf "%s\n" ' cpu name [/usr/gnemul/qemu-%M]' - printf "%s\n" ' --libdir=VALUE Library directory [lib64]' + printf "%s\n" ' --libdir=VALUE Library directory [lib/x86_64-linux-gnu]' printf "%s\n" ' --libexecdir=VALUE Library executable directory [libexec]' printf "%s\n" ' --localedir=VALUE Locale data directory [share/locale]' printf "%s\n" ' --localstatedir=VALUE Localstate data directory [/var/local]' @@ -110,6 +110,7 @@ meson_options_help() { printf "%s\n" ' libssh ssh block device support' printf "%s\n" ' libudev Use libudev to enumerate host devices' printf "%s\n" ' libusb libusb support for USB passthrough' + printf "%s\n" ' libvduse build VDUSE Library' printf "%s\n" ' linux-aio Linux AIO support' printf "%s\n" ' linux-io-uring Linux io_uring support' printf "%s\n" ' live-block-migration' @@ -153,6 +154,8 @@ meson_options_help() { printf "%s\n" ' usb-redir libusbredir support' printf "%s\n" ' vde vde network backend support' printf "%s\n" ' vdi vdi image format support' + printf "%s\n" ' vduse-blk-export' + printf "%s\n" ' VDUSE block export support' printf "%s\n" ' vfio-user-server' printf "%s\n" ' vfio-user server support' printf "%s\n" ' vhost-crypto vhost-user crypto backend support' @@ -307,6 +310,8 @@ _meson_option_parse() { --disable-libudev) printf "%s" -Dlibudev=disabled ;; --enable-libusb) printf "%s" -Dlibusb=enabled ;; --disable-libusb) printf "%s" -Dlibusb=disabled ;; + --enable-libvduse) printf "%s" -Dlibvduse=enabled ;; + --disable-libvduse) printf "%s" -Dlibvduse=disabled ;; --enable-linux-aio) printf "%s" -Dlinux_aio=enabled ;; --disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;; --enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;; @@ -358,7 +363,7 @@ _meson_option_parse() { --disable-qcow1) printf "%s" -Dqcow1=disabled ;; --enable-qed) printf "%s" -Dqed=enabled ;; --disable-qed) printf "%s" -Dqed=disabled ;; - --firmwarepath=*) quote_sh "-Dqemu_firmwarepath=$2" ;; + --firmwarepath=*) quote_sh "-Dqemu_firmwarepath=$(meson_option_build_array $2)" ;; --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 ;; @@ -417,6 +422,8 @@ _meson_option_parse() { --disable-vde) printf "%s" -Dvde=disabled ;; --enable-vdi) printf "%s" -Dvdi=enabled ;; --disable-vdi) printf "%s" -Dvdi=disabled ;; + --enable-vduse-blk-export) printf "%s" -Dvduse_blk_export=enabled ;; + --disable-vduse-blk-export) printf "%s" -Dvduse_blk_export=disabled ;; --enable-vfio-user-server) printf "%s" -Dvfio_user_server=enabled ;; --disable-vfio-user-server) printf "%s" -Dvfio_user_server=disabled ;; --enable-vhost-crypto) printf "%s" -Dvhost_crypto=enabled ;; diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 98b56e05210..2656a89aeae 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # # OSS-Fuzz build script. See: # https://google.github.io/oss-fuzz/getting-started/new-project-guide/#buildsh @@ -64,7 +64,7 @@ mkdir -p "$DEST_DIR/lib/" # Copy the shared libraries here # Build once to get the list of dynamic lib paths, and copy them over ../configure --disable-werror --cc="$CC" --cxx="$CXX" --enable-fuzzing \ - --prefix="$DEST_DIR" --bindir="$DEST_DIR" --datadir="$DEST_DIR/data/" \ + --prefix="/opt/qemu-oss-fuzz" \ --extra-cflags="$EXTRA_CFLAGS" --target-list="i386-softmmu" if ! make "-j$(nproc)" qemu-fuzz-i386; then @@ -81,14 +81,14 @@ if [ "$GITLAB_CI" != "true" ]; then # Build a second time to build the final binary with correct rpath ../configure --disable-werror --cc="$CC" --cxx="$CXX" --enable-fuzzing \ - --prefix="$DEST_DIR" --bindir="$DEST_DIR" --datadir="$DEST_DIR/data/" \ + --prefix="/opt/qemu-oss-fuzz" \ --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="-Wl,-rpath,\$ORIGIN/lib" \ --target-list="i386-softmmu" make "-j$(nproc)" qemu-fuzz-i386 V=1 fi -# Copy over the datadir -cp -r ../pc-bios/ "$DEST_DIR/pc-bios" +# Prepare a preinstalled tree +make install DESTDIR=$DEST_DIR/qemu-bundle targets=$(./qemu-fuzz-i386 | awk '$1 ~ /\*/ {print $2}') base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)" @@ -105,7 +105,7 @@ do # to be configured. We have some generic-fuzz-{pc-q35, floppy, ...} targets # that are thin wrappers around this target that set the required # environment variables according to predefined configs. - if [ "$target" != "generic-fuzz" ]; then + if [[ $target == "generic-fuzz-"* ]]; then ln $base_copy \ "$DEST_DIR/qemu-fuzz-i386-target-$target" fi diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 489273574ae..737b059e629 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -114,7 +114,7 @@ def c_name(name: str, protect: bool = True) -> str: 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: - polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) + polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386', 'linux']) name = re.sub(r'[^A-Za-z0-9_]', '_', name) if protect and (name in (c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 9cb723f4435..6ef9f118d9f 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -4,7 +4,7 @@ qemu_target_list="i386 i486 alpha arm armeb sparc sparc32plus sparc64 \ ppc ppc64 ppc64le m68k mips mipsel mipsn32 mipsn32el mips64 mips64el \ sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \ -microblaze microblazeel or1k x86_64 hexagon" +microblaze microblazeel or1k x86_64 hexagon loongarch64" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -140,6 +140,10 @@ hexagon_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x hexagon_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' hexagon_family=hexagon +loongarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x01' +loongarch64_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +loongarch64_family=loongarch + qemu_get_family() { cpu=${HOST_ARCH:-$(uname -m)} case "$cpu" in @@ -167,6 +171,9 @@ qemu_get_family() { riscv*) echo "riscv" ;; + loongarch*) + echo "loongarch" + ;; *) echo "$cpu" ;; diff --git a/scripts/symlink-install-tree.py b/scripts/symlink-install-tree.py new file mode 100644 index 00000000000..a5bf0b0d6d7 --- /dev/null +++ b/scripts/symlink-install-tree.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +from pathlib import PurePath +import errno +import json +import os +import subprocess +import sys + +def destdir_join(d1: str, d2: str) -> str: + if not d1: + return d2 + # c:\destdir + c:\prefix must produce c:\destdir\prefix + return str(PurePath(d1, *PurePath(d2).parts[1:])) + +introspect = os.environ.get('MESONINTROSPECT') +out = subprocess.run([*introspect.split(' '), '--installed'], + stdout=subprocess.PIPE, check=True).stdout +for source, dest in json.loads(out).items(): + assert os.path.isabs(source) + bundle_dest = destdir_join('qemu-bundle', dest) + path = os.path.dirname(bundle_dest) + try: + os.makedirs(path, exist_ok=True) + except BaseException as e: + print(f'error making directory {path}', file=sys.stderr) + raise e + try: + os.symlink(source, bundle_dest) + except BaseException as e: + if not isinstance(e, OSError) or e.errno != errno.EEXIST: + print(f'error making symbolic link {dest}', file=sys.stderr) + raise e diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 839a5ec6149..b1ad99cba82 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -161,7 +161,7 @@ done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" for header in kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ - psci.h psp-sev.h userfaultfd.h mman.h; do + psci.h psp-sev.h userfaultfd.h mman.h vduse.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index 539ead62b49..b3693883602 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -40,7 +40,7 @@ def check_fields_match(name, s_field, d_field): return True # Some fields changed names between qemu versions. This list - # is used to whitelist such changes in each section / description. + # is used to allow such changes in each section / description. changed_names = { 'apic': ['timer', 'timer_expiry'], 'e1000': ['dev', 'parent_obj'], diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index b6ddaf863ad..1a1e2a69605 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -32,12 +32,14 @@ */ #include "qemu/osdep.h" - +#include "qemu/timer.h" +#include "exec/gdbstub.h" #include "semihosting/semihost.h" #include "semihosting/console.h" #include "semihosting/common-semi.h" -#include "qemu/timer.h" -#include "exec/gdbstub.h" +#include "semihosting/guestfd.h" +#include "semihosting/syscalls.h" + #ifdef CONFIG_USER_ONLY #include "qemu.h" @@ -45,9 +47,6 @@ #else #include "qemu/cutils.h" #include "hw/loader.h" -#ifdef TARGET_ARM -#include "hw/arm/boot.h" -#endif #include "hw/boards.h" #endif @@ -85,65 +84,21 @@ #define O_BINARY 0 #endif -#define GDB_O_RDONLY 0x000 -#define GDB_O_WRONLY 0x001 -#define GDB_O_RDWR 0x002 -#define GDB_O_APPEND 0x008 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_BINARY 0 - static int gdb_open_modeflags[12] = { GDB_O_RDONLY, - GDB_O_RDONLY | GDB_O_BINARY, + GDB_O_RDONLY, GDB_O_RDWR, - GDB_O_RDWR | GDB_O_BINARY, + GDB_O_RDWR, + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, - GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, - GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, - GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY, GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, - GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY -}; - -static int open_modeflags[12] = { - O_RDONLY, - O_RDONLY | O_BINARY, - O_RDWR, - O_RDWR | O_BINARY, - O_WRONLY | O_CREAT | O_TRUNC, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - O_RDWR | O_CREAT | O_TRUNC, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - O_WRONLY | O_CREAT | O_APPEND, - O_WRONLY | O_CREAT | O_APPEND | O_BINARY, - O_RDWR | O_CREAT | O_APPEND, - O_RDWR | O_CREAT | O_APPEND | O_BINARY + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, }; -typedef enum GuestFDType { - GuestFDUnused = 0, - GuestFDHost = 1, - GuestFDGDB = 2, - GuestFDFeatureFile = 3, -} GuestFDType; - -/* - * Guest file descriptors are integer indexes into an array of - * these structures (we will dynamically resize as necessary). - */ -typedef struct GuestFD { - GuestFDType type; - union { - int hostfd; - target_ulong featurefile_offset; - }; -} GuestFD; - -static GArray *guestfd_array; - #ifndef CONFIG_USER_ONLY /** @@ -210,155 +165,31 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif -#ifdef TARGET_ARM -static inline target_ulong -common_semi_arg(CPUState *cs, int argno) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - if (is_a64(env)) { - return env->xregs[argno]; - } else { - return env->regs[argno]; - } -} - -static inline void -common_semi_set_ret(CPUState *cs, target_ulong ret) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - if (is_a64(env)) { - env->xregs[0] = ret; - } else { - env->regs[0] = ret; - } -} - -static inline bool -common_semi_sys_exit_extended(CPUState *cs, int nr) -{ - return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); -} - -#endif /* TARGET_ARM */ - -#ifdef TARGET_RISCV -static inline target_ulong -common_semi_arg(CPUState *cs, int argno) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - return env->gpr[xA0 + argno]; -} - -static inline void -common_semi_set_ret(CPUState *cs, target_ulong ret) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - env->gpr[xA0] = ret; -} - -static inline bool -common_semi_sys_exit_extended(CPUState *cs, int nr) -{ - return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); -} - -#endif - -/* - * Allocate a new guest file descriptor and return it; if we - * couldn't allocate a new fd then return -1. - * This is a fairly simplistic implementation because we don't - * expect that most semihosting guest programs will make very - * heavy use of opening and closing fds. - */ -static int alloc_guestfd(void) -{ - guint i; - - if (!guestfd_array) { - /* New entries zero-initialized, i.e. type GuestFDUnused */ - guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); - } - - /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ - for (i = 1; i < guestfd_array->len; i++) { - GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); - - if (gf->type == GuestFDUnused) { - return i; - } - } - - /* All elements already in use: expand the array */ - g_array_set_size(guestfd_array, i + 1); - return i; -} - -/* - * Look up the guestfd in the data structure; return NULL - * for out of bounds, but don't check whether the slot is unused. - * This is used internally by the other guestfd functions. - */ -static GuestFD *do_get_guestfd(int guestfd) -{ - if (!guestfd_array) { - return NULL; - } - - if (guestfd <= 0 || guestfd >= guestfd_array->len) { - return NULL; - } - - return &g_array_index(guestfd_array, GuestFD, guestfd); -} - -/* - * Associate the specified guest fd (which must have been - * allocated via alloc_fd() and not previously used) with - * the specified host/gdb fd. - */ -static void associate_guestfd(int guestfd, int hostfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; - gf->hostfd = hostfd; -} +#include "common-semi-target.h" /* - * Deallocate the specified guest file descriptor. This doesn't - * close the host fd, it merely undoes the work of alloc_fd(). + * Read the input value from the argument block; fail the semihosting + * call if the memory read fails. Eventually we could use a generic + * CPUState helper function here. */ -static void dealloc_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - assert(gf); - gf->type = GuestFDUnused; -} +#define GET_ARG(n) do { \ + if (is_64bit_semihosting(env)) { \ + if (get_user_u64(arg ## n, args + (n) * 8)) { \ + goto do_fault; \ + } \ + } else { \ + if (get_user_u32(arg ## n, args + (n) * 4)) { \ + goto do_fault; \ + } \ + } \ +} while (0) -/* - * Given a guest file descriptor, get the associated struct. - * If the fd is not valid, return NULL. This is the function - * used by the various semihosting calls to validate a handle - * from the guest. - * Note: calling alloc_guestfd() or dealloc_guestfd() will - * invalidate any GuestFD* obtained by calling this function. - */ -static GuestFD *get_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); +#define SET_ARG(n, val) \ + (is_64bit_semihosting(env) ? \ + put_user_u64(val, args + (n) * 8) : \ + put_user_u32(val, args + (n) * 4)) - if (!gf || gf->type == GuestFDUnused) { - return NULL; - } - return gf; -} /* * The semihosting API has no concept of its errno being thread-safe, @@ -370,22 +201,8 @@ static GuestFD *get_guestfd(int guestfd) #ifndef CONFIG_USER_ONLY static target_ulong syscall_err; -#include "exec/softmmu-semi.h" -#endif - -static inline uint32_t set_swi_errno(CPUState *cs, uint32_t code) -{ - if (code == (uint32_t)-1) { -#ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; - - ts->swi_errno = errno; -#else - syscall_err = errno; +#include "semihosting/softmmu-uaccess.h" #endif - } - return code; -} static inline uint32_t get_swi_errno(CPUState *cs) { @@ -398,254 +215,116 @@ static inline uint32_t get_swi_errno(CPUState *cs) #endif } -static target_ulong common_semi_syscall_len; - -static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - target_ulong reg0 = common_semi_arg(cs, 0); - - if (ret == (target_ulong)-1) { - errno = err; - set_swi_errno(cs, -1); - reg0 = ret; - } else { - /* Fixup syscalls that use nonstardard return conventions. */ - switch (reg0) { - case TARGET_SYS_WRITE: - case TARGET_SYS_READ: - reg0 = common_semi_syscall_len - ret; - break; - case TARGET_SYS_SEEK: - reg0 = 0; - break; - default: - reg0 = ret; - break; - } - } - common_semi_set_ret(cs, reg0); -} - -static target_ulong common_semi_flen_buf(CPUState *cs) +static void common_semi_cb(CPUState *cs, uint64_t ret, int err) { - target_ulong sp; -#ifdef TARGET_ARM - /* Return an address in target memory of 64 bytes where the remote - * gdb should write its stat struct. (The format of this structure - * is defined by GDB's remote protocol and is not target-specific.) - * We put this on the guest's stack just below SP. - */ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - if (is_a64(env)) { - sp = env->xregs[31]; - } else { - sp = env->regs[13]; - } -#endif -#ifdef TARGET_RISCV - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - - sp = env->gpr[xSP]; + if (err) { +#ifdef CONFIG_USER_ONLY + TaskState *ts = cs->opaque; + ts->swi_errno = err; +#else + syscall_err = err; #endif - - return sp - 64; -} - -static void -common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - /* The size is always stored in big-endian order, extract - the value. We assume the size always fit in 32 bits. */ - uint32_t size; - cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + 32, - (uint8_t *)&size, 4, 0); - size = be32_to_cpu(size); - common_semi_set_ret(cs, size); - errno = err; - set_swi_errno(cs, -1); -} - -static int common_semi_open_guestfd; - -static void -common_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - if (ret == (target_ulong)-1) { - errno = err; - set_swi_errno(cs, -1); - dealloc_guestfd(common_semi_open_guestfd); - } else { - associate_guestfd(common_semi_open_guestfd, ret); - ret = common_semi_open_guestfd; } common_semi_set_ret(cs, ret); } -static target_ulong -common_semi_gdb_syscall(CPUState *cs, gdb_syscall_complete_cb cb, - const char *fmt, ...) +/* + * Use 0xdeadbeef as the return value when there isn't a defined + * return value for the call. + */ +static void common_semi_dead_cb(CPUState *cs, uint64_t ret, int err) { - va_list va; - - va_start(va, fmt); - gdb_do_syscallv(cb, fmt, va); - va_end(va); - - /* - * FIXME: in softmmu mode, the gdbstub will schedule our callback - * to occur, but will not actually call it to complete the syscall - * until after this function has returned and we are back in the - * CPU main loop. Therefore callers to this function must not - * do anything with its return value, because it is not necessarily - * the result of the syscall, but could just be the old value of X0. - * The only thing safe to do with this is that the callers of - * do_common_semihosting() will write it straight back into X0. - * (In linux-user mode, the callback will have happened before - * gdb_do_syscallv() returns.) - * - * We should tidy this up so neither this function nor - * do_common_semihosting() return a value, so the mistake of - * doing something with the return value is not possible to make. - */ - - return common_semi_arg(cs, 0); + common_semi_set_ret(cs, 0xdeadbeef); } /* - * Types for functions implementing various semihosting calls - * for specific types of guest file descriptor. These must all - * do the work and return the required return value for the guest, - * setting the guest errno if appropriate. + * SYS_READ and SYS_WRITE always return the number of bytes not read/written. + * There is no error condition, other than returning the original length. */ -typedef uint32_t sys_closefn(CPUState *cs, GuestFD *gf); -typedef uint32_t sys_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); -typedef uint32_t sys_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); -typedef uint32_t sys_isattyfn(CPUState *cs, GuestFD *gf); -typedef uint32_t sys_seekfn(CPUState *cs, GuestFD *gf, - target_ulong offset); -typedef uint32_t sys_flenfn(CPUState *cs, GuestFD *gf); - -static uint32_t host_closefn(CPUState *cs, GuestFD *gf) +static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err) { - /* - * Only close the underlying host fd if it's one we opened on behalf - * of the guest in SYS_OPEN. - */ - if (gf->hostfd == STDIN_FILENO || - gf->hostfd == STDOUT_FILENO || - gf->hostfd == STDERR_FILENO) { - return 0; + /* Recover the original length from the third argument. */ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + target_ulong args = common_semi_arg(cs, 1); + target_ulong arg2; + GET_ARG(2); + + if (err) { + do_fault: + ret = 0; /* error: no bytes transmitted */ } - return set_swi_errno(cs, close(gf->hostfd)); + common_semi_set_ret(cs, arg2 - ret); } -static uint32_t host_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +/* + * Convert from Posix ret+errno to Arm SYS_ISTTY return values. + * With gdbstub, err is only ever set for protocol errors to EIO. + */ +static void common_semi_istty_cb(CPUState *cs, uint64_t ret, int err) { - CPUArchState *env = cs->env_ptr; - uint32_t ret; - char *s = lock_user(VERIFY_READ, buf, len, 1); - (void) env; /* Used in arm softmmu lock_user implicitly */ - if (!s) { - /* Return bytes not written on error */ - return len; + if (err) { + ret = (err == ENOTTY ? 0 : -1); } - ret = set_swi_errno(cs, write(gf->hostfd, s, len)); - unlock_user(s, buf, 0); - if (ret == (uint32_t)-1) { - ret = 0; - } - /* Return bytes not written */ - return len - ret; + common_semi_cb(cs, ret, err); } -static uint32_t host_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +/* + * SYS_SEEK returns 0 on success, not the resulting offset. + */ +static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err) { - CPUArchState *env = cs->env_ptr; - uint32_t ret; - char *s = lock_user(VERIFY_WRITE, buf, len, 0); - (void) env; /* Used in arm softmmu lock_user implicitly */ - if (!s) { - /* return bytes not read */ - return len; - } - do { - ret = set_swi_errno(cs, read(gf->hostfd, s, len)); - } while (ret == -1 && errno == EINTR); - unlock_user(s, buf, len); - if (ret == (uint32_t)-1) { + if (!err) { ret = 0; } - /* Return bytes not read */ - return len - ret; -} - -static uint32_t host_isattyfn(CPUState *cs, GuestFD *gf) -{ - return isatty(gf->hostfd); + common_semi_cb(cs, ret, err); } -static uint32_t host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) +/* + * Return an address in target memory of 64 bytes where the remote + * gdb should write its stat struct. (The format of this structure + * is defined by GDB's remote protocol and is not target-specific.) + * We put this on the guest's stack just below SP. + */ +static target_ulong common_semi_flen_buf(CPUState *cs) { - uint32_t ret = set_swi_errno(cs, lseek(gf->hostfd, offset, SEEK_SET)); - if (ret == (uint32_t)-1) { - return -1; - } - return 0; + target_ulong sp = common_semi_stack_bottom(cs); + return sp - 64; } -static uint32_t host_flenfn(CPUState *cs, GuestFD *gf) -{ - struct stat buf; - uint32_t ret = set_swi_errno(cs, fstat(gf->hostfd, &buf)); - if (ret == (uint32_t)-1) { - return -1; +static void +common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { + /* The size is always stored in big-endian order, extract the value. */ + uint64_t size; + if (cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + + offsetof(struct gdb_stat, gdb_st_size), + &size, 8, 0)) { + ret = -1, err = EFAULT; + } else { + size = be64_to_cpu(size); + if (ret != size) { + ret = -1, err = EOVERFLOW; + } + } } - return buf.st_size; -} - -static uint32_t gdb_closefn(CPUState *cs, GuestFD *gf) -{ - return common_semi_gdb_syscall(cs, common_semi_cb, "close,%x", gf->hostfd); -} - -static uint32_t gdb_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - common_semi_syscall_len = len; - return common_semi_gdb_syscall(cs, common_semi_cb, "write,%x,%x,%x", - gf->hostfd, buf, len); -} - -static uint32_t gdb_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - common_semi_syscall_len = len; - return common_semi_gdb_syscall(cs, common_semi_cb, "read,%x,%x,%x", - gf->hostfd, buf, len); -} - -static uint32_t gdb_isattyfn(CPUState *cs, GuestFD *gf) -{ - return common_semi_gdb_syscall(cs, common_semi_cb, "isatty,%x", gf->hostfd); + common_semi_cb(cs, ret, err); } -static uint32_t gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) +static void +common_semi_readc_cb(CPUState *cs, uint64_t ret, int err) { - return common_semi_gdb_syscall(cs, common_semi_cb, "lseek,%x,%x,0", - gf->hostfd, offset); -} + if (!err) { + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + uint8_t ch; -static uint32_t gdb_flenfn(CPUState *cs, GuestFD *gf) -{ - return common_semi_gdb_syscall(cs, common_semi_flen_cb, "fstat,%x,%x", - gf->hostfd, common_semi_flen_buf(cs)); + if (get_user_u8(ch, common_semi_stack_bottom(cs) - 1)) { + ret = -1, err = EFAULT; + } else { + ret = ch; + } + } + common_semi_cb(cs, ret, err); } #define SHFB_MAGIC_0 0x53 @@ -665,157 +344,15 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void init_featurefile_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = GuestFDFeatureFile; - gf->featurefile_offset = 0; -} - -static uint32_t featurefile_closefn(CPUState *cs, GuestFD *gf) -{ - /* Nothing to do */ - return 0; -} - -static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - /* This fd can never be open for writing */ - - errno = EBADF; - return set_swi_errno(cs, -1); -} - -static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - CPUArchState *env = cs->env_ptr; - uint32_t i; - char *s; - - (void) env; /* Used in arm softmmu lock_user implicitly */ - s = lock_user(VERIFY_WRITE, buf, len, 0); - if (!s) { - return len; - } - - for (i = 0; i < len; i++) { - if (gf->featurefile_offset >= sizeof(featurefile_data)) { - break; - } - s[i] = featurefile_data[gf->featurefile_offset]; - gf->featurefile_offset++; - } - - unlock_user(s, buf, len); - - /* Return number of bytes not read */ - return len - i; -} - -static uint32_t featurefile_isattyfn(CPUState *cs, GuestFD *gf) -{ - return 0; -} - -static uint32_t featurefile_seekfn(CPUState *cs, GuestFD *gf, - target_ulong offset) -{ - gf->featurefile_offset = offset; - return 0; -} - -static uint32_t featurefile_flenfn(CPUState *cs, GuestFD *gf) -{ - return sizeof(featurefile_data); -} - -typedef struct GuestFDFunctions { - sys_closefn *closefn; - sys_writefn *writefn; - sys_readfn *readfn; - sys_isattyfn *isattyfn; - sys_seekfn *seekfn; - sys_flenfn *flenfn; -} GuestFDFunctions; - -static const GuestFDFunctions guestfd_fns[] = { - [GuestFDHost] = { - .closefn = host_closefn, - .writefn = host_writefn, - .readfn = host_readfn, - .isattyfn = host_isattyfn, - .seekfn = host_seekfn, - .flenfn = host_flenfn, - }, - [GuestFDGDB] = { - .closefn = gdb_closefn, - .writefn = gdb_writefn, - .readfn = gdb_readfn, - .isattyfn = gdb_isattyfn, - .seekfn = gdb_seekfn, - .flenfn = gdb_flenfn, - }, - [GuestFDFeatureFile] = { - .closefn = featurefile_closefn, - .writefn = featurefile_writefn, - .readfn = featurefile_readfn, - .isattyfn = featurefile_isattyfn, - .seekfn = featurefile_seekfn, - .flenfn = featurefile_flenfn, - }, -}; - -/* - * Read the input value from the argument block; fail the semihosting - * call if the memory read fails. Eventually we could use a generic - * CPUState helper function here. - */ -static inline bool is_64bit_semihosting(CPUArchState *env) -{ -#if defined(TARGET_ARM) - return is_a64(env); -#elif defined(TARGET_RISCV) - return riscv_cpu_mxl(env) != MXL_RV32; -#else -#error un-handled architecture -#endif -} - - -#define GET_ARG(n) do { \ - if (is_64bit_semihosting(env)) { \ - if (get_user_u64(arg ## n, args + (n) * 8)) { \ - errno = EFAULT; \ - return set_swi_errno(cs, -1); \ - } \ - } else { \ - if (get_user_u32(arg ## n, args + (n) * 4)) { \ - errno = EFAULT; \ - return set_swi_errno(cs, -1); \ - } \ - } \ -} while (0) - -#define SET_ARG(n, val) \ - (is_64bit_semihosting(env) ? \ - put_user_u64(val, args + (n) * 8) : \ - put_user_u32(val, args + (n) * 4)) - - /* * Do a semihosting call. * * The specification always says that the "return register" either * returns a specific value or is corrupted, so we don't need to * report to our caller whether we are returning a value or trying to - * leave the register unchanged. We use 0xdeadbeef as the return value - * when there isn't a defined return value for the call. + * leave the register unchanged. */ -target_ulong do_common_semihosting(CPUState *cs) +void do_common_semihosting(CPUState *cs) { CPUArchState *env = cs->env_ptr; target_ulong args; @@ -824,43 +361,31 @@ target_ulong do_common_semihosting(CPUState *cs) char * s; int nr; uint32_t ret; - uint32_t len; - GuestFD *gf; int64_t elapsed; - (void) env; /* Used implicitly by arm lock_user macro */ nr = common_semi_arg(cs, 0) & 0xffffffffU; args = common_semi_arg(cs, 1); switch (nr) { case TARGET_SYS_OPEN: { - int guestfd; + int ret, err = 0; + int hostfd; GET_ARG(0); GET_ARG(1); GET_ARG(2); s = lock_user_string(arg0); if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } if (arg1 >= 12) { unlock_user(s, arg0, 0); - errno = EINVAL; - return set_swi_errno(cs, -1); - } - - guestfd = alloc_guestfd(); - if (guestfd < 0) { - unlock_user(s, arg0, 0); - errno = EMFILE; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, EINVAL); + break; } if (strcmp(s, ":tt") == 0) { - int result_fileno; - /* * We implement SH_EXT_STDOUT_STDERR, so: * open for read == stdin @@ -868,206 +393,160 @@ target_ulong do_common_semihosting(CPUState *cs) * open for append == stderr */ if (arg1 < 4) { - result_fileno = STDIN_FILENO; + hostfd = STDIN_FILENO; } else if (arg1 < 8) { - result_fileno = STDOUT_FILENO; + hostfd = STDOUT_FILENO; } else { - result_fileno = STDERR_FILENO; + hostfd = STDERR_FILENO; } - associate_guestfd(guestfd, result_fileno); - unlock_user(s, arg0, 0); - return guestfd; - } - if (strcmp(s, ":semihosting-features") == 0) { - unlock_user(s, arg0, 0); + ret = alloc_guestfd(); + associate_guestfd(ret, hostfd); + } else if (strcmp(s, ":semihosting-features") == 0) { /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */ if (arg1 != 0 && arg1 != 1) { - dealloc_guestfd(guestfd); - errno = EACCES; - return set_swi_errno(cs, -1); - } - init_featurefile_guestfd(guestfd); - return guestfd; - } - - if (use_gdb_syscalls()) { - common_semi_open_guestfd = guestfd; - ret = common_semi_gdb_syscall(cs, common_semi_open_cb, - "open,%s,%x,1a4", arg0, (int)arg2 + 1, - gdb_open_modeflags[arg1]); - } else { - ret = set_swi_errno(cs, open(s, open_modeflags[arg1], 0644)); - if (ret == (uint32_t)-1) { - dealloc_guestfd(guestfd); + ret = -1; + err = EACCES; } else { - associate_guestfd(guestfd, ret); - ret = guestfd; + ret = alloc_guestfd(); + staticfile_guestfd(ret, featurefile_data, + sizeof(featurefile_data)); } + } else { + unlock_user(s, arg0, 0); + semihost_sys_open(cs, common_semi_cb, arg0, arg2 + 1, + gdb_open_modeflags[arg1], 0644); + break; } unlock_user(s, arg0, 0); - return ret; + common_semi_cb(cs, ret, err); + break; } + case TARGET_SYS_CLOSE: GET_ARG(0); + semihost_sys_close(cs, common_semi_cb, arg0); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - ret = guestfd_fns[gf->type].closefn(cs, gf); - dealloc_guestfd(arg0); - return ret; case TARGET_SYS_WRITEC: - qemu_semihosting_console_outc(cs->env_ptr, args); - return 0xdeadbeef; + /* + * FIXME: the byte to be written is in a target_ulong slot, + * which means this is wrong for a big-endian guest. + */ + semihost_sys_write_gf(cs, common_semi_dead_cb, + &console_out_gf, args, 1); + break; + case TARGET_SYS_WRITE0: - return qemu_semihosting_console_outs(cs->env_ptr, args); + { + ssize_t len = target_strlen(args); + if (len < 0) { + common_semi_dead_cb(cs, -1, EFAULT); + } else { + semihost_sys_write_gf(cs, common_semi_dead_cb, + &console_out_gf, args, len); + } + } + break; + case TARGET_SYS_WRITE: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } + semihost_sys_write(cs, common_semi_rw_cb, arg0, arg1, arg2); + break; - return guestfd_fns[gf->type].writefn(cs, gf, arg1, len); case TARGET_SYS_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } + semihost_sys_read(cs, common_semi_rw_cb, arg0, arg1, arg2); + break; - return guestfd_fns[gf->type].readfn(cs, gf, arg1, len); case TARGET_SYS_READC: - return qemu_semihosting_console_inc(cs->env_ptr); + semihost_sys_read_gf(cs, common_semi_readc_cb, &console_in_gf, + common_semi_stack_bottom(cs) - 1, 1); + break; + case TARGET_SYS_ISERROR: GET_ARG(0); - return (target_long) arg0 < 0 ? 1 : 0; + common_semi_set_ret(cs, (target_long)arg0 < 0); + break; + case TARGET_SYS_ISTTY: GET_ARG(0); + semihost_sys_isatty(cs, common_semi_istty_cb, arg0); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].isattyfn(cs, gf); case TARGET_SYS_SEEK: GET_ARG(0); GET_ARG(1); + semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].seekfn(cs, gf, arg1); case TARGET_SYS_FLEN: GET_ARG(0); + semihost_sys_flen(cs, common_semi_flen_fstat_cb, common_semi_cb, + arg0, common_semi_flen_buf(cs)); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].flenfn(cs, gf); case TARGET_SYS_TMPNAM: + { + int len; + char *p; + GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (asprintf(&s, "/tmp/qemu-%x%02x", getpid(), - (int) (arg1 & 0xff)) < 0) { - return -1; - } - ul_ret = (target_ulong) -1; - + len = asprintf(&s, "/tmp/qemu-%x%02x", getpid(), (int)arg1 & 0xff); /* Make sure there's enough space in the buffer */ - if (strlen(s) < arg2) { - char *output = lock_user(VERIFY_WRITE, arg0, arg2, 0); - strcpy(output, s); - unlock_user(output, arg0, arg2); - ul_ret = 0; + if (len < 0 || len >= arg2) { + common_semi_set_ret(cs, -1); + break; } + p = lock_user(VERIFY_WRITE, arg0, len, 0); + if (!p) { + goto do_fault; + } + memcpy(p, s, len + 1); + unlock_user(p, arg0, len); free(s); - return ul_ret; + common_semi_set_ret(cs, 0); + break; + } + case TARGET_SYS_REMOVE: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - ret = common_semi_gdb_syscall(cs, common_semi_cb, "unlink,%s", - arg0, (int)arg1 + 1); - } else { - s = lock_user_string(arg0); - if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); - } - ret = set_swi_errno(cs, remove(s)); - unlock_user(s, arg0, 0); - } - return ret; + semihost_sys_remove(cs, common_semi_cb, arg0, arg1 + 1); + break; + case TARGET_SYS_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - return common_semi_gdb_syscall(cs, common_semi_cb, "rename,%s,%s", - arg0, (int)arg1 + 1, arg2, - (int)arg3 + 1); - } else { - char *s2; - s = lock_user_string(arg0); - s2 = lock_user_string(arg2); - if (!s || !s2) { - errno = EFAULT; - ret = set_swi_errno(cs, -1); - } else { - ret = set_swi_errno(cs, rename(s, s2)); - } - if (s2) - unlock_user(s2, arg2, 0); - if (s) - unlock_user(s, arg0, 0); - return ret; - } + semihost_sys_rename(cs, common_semi_cb, arg0, arg1 + 1, arg2, arg3 + 1); + break; + case TARGET_SYS_CLOCK: - return clock() / (CLOCKS_PER_SEC / 100); + common_semi_set_ret(cs, clock() / (CLOCKS_PER_SEC / 100)); + break; + case TARGET_SYS_TIME: - return set_swi_errno(cs, time(NULL)); + ul_ret = time(NULL); + common_semi_cb(cs, ul_ret, ul_ret == -1 ? errno : 0); + break; + case TARGET_SYS_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - return common_semi_gdb_syscall(cs, common_semi_cb, "system,%s", - arg0, (int)arg1 + 1); - } else { - s = lock_user_string(arg0); - if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); - } - ret = set_swi_errno(cs, system(s)); - unlock_user(s, arg0, 0); - return ret; - } + semihost_sys_system(cs, common_semi_cb, arg0, arg1 + 1); + break; + case TARGET_SYS_ERRNO: - return get_swi_errno(cs); + common_semi_set_ret(cs, get_swi_errno(cs)); + break; + case TARGET_SYS_GET_CMDLINE: { /* Build a command-line from the original argv. @@ -1118,22 +597,20 @@ target_ulong do_common_semihosting(CPUState *cs) if (output_size > input_size) { /* Not enough space to store command-line arguments. */ - errno = E2BIG; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, E2BIG); + break; } /* Adjust the command-line length. */ if (SET_ARG(1, output_size - 1)) { /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } /* Lock the buffer on the ARM side. */ output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0); if (!output_buffer) { - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } /* Copy the command-line arguments. */ @@ -1148,9 +625,8 @@ target_ulong do_common_semihosting(CPUState *cs) if (copy_from_user(output_buffer, ts->info->arg_strings, output_size)) { - errno = EFAULT; - status = set_swi_errno(cs, -1); - goto out; + unlock_user(output_buffer, arg0, 0); + goto do_fault; } /* Separate arguments by white spaces. */ @@ -1163,9 +639,10 @@ target_ulong do_common_semihosting(CPUState *cs) #endif /* Unlock the buffer on the ARM side. */ unlock_user(output_buffer, arg0, output_size); - - return status; + common_semi_cb(cs, status, 0); } + break; + case TARGET_SYS_HEAPINFO: { target_ulong retvals[4]; @@ -1222,12 +699,13 @@ target_ulong do_common_semihosting(CPUState *cs) if (fail) { /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } } - return 0; + common_semi_set_ret(cs, 0); } + break; + case TARGET_SYS_EXIT: case TARGET_SYS_EXIT_EXTENDED: if (common_semi_sys_exit_extended(cs, nr)) { @@ -1257,6 +735,7 @@ target_ulong do_common_semihosting(CPUState *cs) } gdb_exit(ret); exit(ret); + case TARGET_SYS_ELAPSED: elapsed = get_clock() - clock_start; if (sizeof(target_ulong) == 8) { @@ -1265,28 +744,32 @@ target_ulong do_common_semihosting(CPUState *cs) SET_ARG(0, (uint32_t) elapsed); SET_ARG(1, (uint32_t) (elapsed >> 32)); } - return 0; + common_semi_set_ret(cs, 0); + break; + case TARGET_SYS_TICKFREQ: /* qemu always uses nsec */ - return 1000000000; + common_semi_set_ret(cs, 1000000000); + break; + case TARGET_SYS_SYNCCACHE: /* * Clean the D-cache and invalidate the I-cache for the specified * virtual address range. This is a nop for us since we don't * implement caches. This is only present on A64. */ -#ifdef TARGET_ARM - if (is_a64(cs->env_ptr)) { - return 0; + if (common_semi_has_synccache(env)) { + common_semi_set_ret(cs, 0); + break; } -#endif -#ifdef TARGET_RISCV - return 0; -#endif - /* fall through -- invalid for A32/T32 */ + /* fall through */ default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, 0); abort(); + + do_fault: + common_semi_cb(cs, -1, EFAULT); + break; } } diff --git a/semihosting/config.c b/semihosting/config.c index 3afacf54ab2..e171d4d6bc3 100644 --- a/semihosting/config.c +++ b/semihosting/config.c @@ -51,7 +51,6 @@ QemuOptsList qemu_semihosting_config_opts = { typedef struct SemihostingConfig { bool enabled; SemihostingTarget target; - Chardev *chardev; char **argv; int argc; const char *cmdline; /* concatenated argv */ @@ -122,11 +121,6 @@ void semihosting_arg_fallback(const char *file, const char *cmd) } } -Chardev *semihosting_get_chardev(void) -{ - return semihosting.chardev; -} - void qemu_semihosting_enable(void) { semihosting.enabled = true; @@ -172,16 +166,19 @@ int qemu_semihosting_config_options(const char *optarg) return 0; } -void qemu_semihosting_connect_chardevs(void) +/* We had to defer this until chardevs were created */ +void qemu_semihosting_chardev_init(void) { - /* We had to defer this until chardevs were created */ + Chardev *chr = NULL; + if (semihost_chardev) { - Chardev *chr = qemu_chr_find(semihost_chardev); + chr = qemu_chr_find(semihost_chardev); if (chr == NULL) { error_report("semihosting chardev '%s' not found", semihost_chardev); exit(1); } - semihosting.chardev = chr; } + + qemu_semihosting_console_init(chr); } diff --git a/semihosting/console.c b/semihosting/console.c index ef6958d8445..5b1ec0a1c39 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -27,89 +27,10 @@ #include "qapi/error.h" #include "qemu/fifo8.h" -int qemu_semihosting_log_out(const char *s, int len) -{ - Chardev *chardev = semihosting_get_chardev(); - if (chardev) { - return qemu_chr_write_all(chardev, (uint8_t *) s, len); - } else { - return write(STDERR_FILENO, s, len); - } -} - -/* - * A re-implementation of lock_user_string that we can use locally - * instead of relying on softmmu-semi. Hopefully we can deprecate that - * in time. Copy string until we find a 0 or address error. - */ -static GString *copy_user_string(CPUArchState *env, target_ulong addr) -{ - CPUState *cpu = env_cpu(env); - GString *s = g_string_sized_new(128); - uint8_t c; - - do { - if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) { - if (c) { - s = g_string_append_c(s, c); - } - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - break; - } - } while (c!=0); - - return s; -} - -static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - if (ret == (target_ulong) -1) { - qemu_log("%s: gdb console output failed ("TARGET_FMT_ld")", - __func__, err); - } -} - -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) -{ - GString *s = copy_user_string(env, addr); - int out = s->len; - - if (use_gdb_syscalls()) { - gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len); - } else { - out = qemu_semihosting_log_out(s->str, s->len); - } - - g_string_free(s, true); - return out; -} - -void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) -{ - CPUState *cpu = env_cpu(env); - uint8_t c; - - if (cpu_memory_rw_debug(cpu, addr, &c, 1, 0) == 0) { - if (use_gdb_syscalls()) { - gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, 1); - } else { - qemu_semihosting_log_out((const char *) &c, 1); - } - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - } -} - -#define FIFO_SIZE 1024 - /* Access to this structure is protected by the BQL */ typedef struct SemihostingConsole { CharBackend backend; + Chardev *chr; GSList *sleeping_cpus; bool got; Fifo8 fifo; @@ -117,6 +38,8 @@ typedef struct SemihostingConsole { static SemihostingConsole console; +#define FIFO_SIZE 1024 + static int console_can_read(void *opaque) { SemihostingConsole *c = opaque; @@ -145,27 +68,58 @@ static void console_read(void *opaque, const uint8_t *buf, int size) c->sleeping_cpus = NULL; } -target_ulong qemu_semihosting_console_inc(CPUArchState *env) +bool qemu_semihosting_console_ready(void) +{ + SemihostingConsole *c = &console; + + g_assert(qemu_mutex_iothread_locked()); + return !fifo8_is_empty(&c->fifo); +} + +void qemu_semihosting_console_block_until_ready(CPUState *cs) { - uint8_t ch; SemihostingConsole *c = &console; + g_assert(qemu_mutex_iothread_locked()); - g_assert(current_cpu); + + /* Block if the fifo is completely empty. */ if (fifo8_is_empty(&c->fifo)) { - c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, current_cpu); - current_cpu->halted = 1; - current_cpu->exception_index = EXCP_HALTED; - cpu_loop_exit(current_cpu); + c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); + cs->halted = 1; + cs->exception_index = EXCP_HALTED; + cpu_loop_exit(cs); /* never returns */ } - ch = fifo8_pop(&c->fifo); - return (target_ulong) ch; } -void qemu_semihosting_console_init(void) +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) { - Chardev *chr = semihosting_get_chardev(); + SemihostingConsole *c = &console; + int ret = 0; + qemu_semihosting_console_block_until_ready(cs); + + /* Read until buffer full or fifo exhausted. */ + do { + *(char *)(buf + ret) = fifo8_pop(&c->fifo); + ret++; + } while (ret < len && !fifo8_is_empty(&c->fifo)); + + return ret; +} + +int qemu_semihosting_console_write(void *buf, int len) +{ + if (console.chr) { + return qemu_chr_write_all(console.chr, (uint8_t *)buf, len); + } else { + return fwrite(buf, 1, len, stderr); + } +} + +void qemu_semihosting_console_init(Chardev *chr) +{ + console.chr = chr; if (chr) { fifo8_create(&console.fifo, FIFO_SIZE); qemu_chr_fe_init(&console.backend, chr, &error_abort); @@ -175,4 +129,6 @@ void qemu_semihosting_console_init(void) NULL, NULL, &console, NULL, true); } + + qemu_semihosting_guestfd_init(); } diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c new file mode 100644 index 00000000000..b05c52f26ff --- /dev/null +++ b/semihosting/guestfd.c @@ -0,0 +1,160 @@ +/* + * Hosted file support for semihosting syscalls. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/gdbstub.h" +#include "semihosting/semihost.h" +#include "semihosting/guestfd.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "semihosting/softmmu-uaccess.h" +#include CONFIG_DEVICES +#endif + +static GArray *guestfd_array; + +#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING +GuestFD console_in_gf; +GuestFD console_out_gf; +#endif + +void qemu_semihosting_guestfd_init(void) +{ + /* New entries zero-initialized, i.e. type GuestFDUnused */ + guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); + +#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING + /* For ARM-compat, the console is in a separate namespace. */ + if (use_gdb_syscalls()) { + console_in_gf.type = GuestFDGDB; + console_in_gf.hostfd = 0; + console_out_gf.type = GuestFDGDB; + console_out_gf.hostfd = 2; + } else { + console_in_gf.type = GuestFDConsole; + console_out_gf.type = GuestFDConsole; + } +#else + /* Otherwise, the stdio file descriptors apply. */ + guestfd_array = g_array_set_size(guestfd_array, 3); +#ifndef CONFIG_USER_ONLY + if (!use_gdb_syscalls()) { + GuestFD *gf = &g_array_index(guestfd_array, GuestFD, 0); + gf[0].type = GuestFDConsole; + gf[1].type = GuestFDConsole; + gf[2].type = GuestFDConsole; + return; + } +#endif + associate_guestfd(0, 0); + associate_guestfd(1, 1); + associate_guestfd(2, 2); +#endif +} + +/* + * Allocate a new guest file descriptor and return it; if we + * couldn't allocate a new fd then return -1. + * This is a fairly simplistic implementation because we don't + * expect that most semihosting guest programs will make very + * heavy use of opening and closing fds. + */ +int alloc_guestfd(void) +{ + guint i; + + /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ + for (i = 1; i < guestfd_array->len; i++) { + GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); + + if (gf->type == GuestFDUnused) { + return i; + } + } + + /* All elements already in use: expand the array */ + g_array_set_size(guestfd_array, i + 1); + return i; +} + +static void do_dealloc_guestfd(GuestFD *gf) +{ + gf->type = GuestFDUnused; +} + +/* + * Look up the guestfd in the data structure; return NULL + * for out of bounds, but don't check whether the slot is unused. + * This is used internally by the other guestfd functions. + */ +static GuestFD *do_get_guestfd(int guestfd) +{ + if (guestfd < 0 || guestfd >= guestfd_array->len) { + return NULL; + } + + return &g_array_index(guestfd_array, GuestFD, guestfd); +} + +/* + * Given a guest file descriptor, get the associated struct. + * If the fd is not valid, return NULL. This is the function + * used by the various semihosting calls to validate a handle + * from the guest. + * Note: calling alloc_guestfd() or dealloc_guestfd() will + * invalidate any GuestFD* obtained by calling this function. + */ +GuestFD *get_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + if (!gf || gf->type == GuestFDUnused) { + return NULL; + } + return gf; +} + +/* + * Associate the specified guest fd (which must have been + * allocated via alloc_fd() and not previously used) with + * the specified host/gdb fd. + */ +void associate_guestfd(int guestfd, int hostfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; + gf->hostfd = hostfd; +} + +void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = GuestFDStatic; + gf->staticfile.data = data; + gf->staticfile.len = len; + gf->staticfile.off = 0; +} + +/* + * Deallocate the specified guest file descriptor. This doesn't + * close the host fd, it merely undoes the work of alloc_fd(). + */ +void dealloc_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + do_dealloc_guestfd(gf); +} diff --git a/semihosting/meson.build b/semihosting/meson.build index ea8090abe34..8057db5494a 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,6 +1,12 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( + 'guestfd.c', + 'syscalls.c', +)) + +specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( 'config.c', 'console.c', + 'uaccess.c', )) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c new file mode 100644 index 00000000000..4847f66c023 --- /dev/null +++ b/semihosting/syscalls.c @@ -0,0 +1,978 @@ +/* + * Syscall implementations for semihosting. + * + * Copyright (c) 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/gdbstub.h" +#include "semihosting/guestfd.h" +#include "semihosting/syscalls.h" +#include "semihosting/console.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "semihosting/softmmu-uaccess.h" +#endif + + +/* + * Validate or compute the length of the string (including terminator). + */ +static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char c; + + if (tlen == 0) { + ssize_t slen = target_strlen(str); + + if (slen < 0) { + return -EFAULT; + } + if (slen >= INT32_MAX) { + return -ENAMETOOLONG; + } + return slen + 1; + } + if (tlen > INT32_MAX) { + return -ENAMETOOLONG; + } + if (get_user_u8(c, str + tlen - 1)) { + return -EFAULT; + } + if (c != 0) { + return -EINVAL; + } + return tlen; +} + +static int validate_lock_user_string(char **pstr, CPUState *cs, + target_ulong tstr, target_ulong tlen) +{ + int ret = validate_strlen(cs, tstr, tlen); + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *str = NULL; + + if (ret > 0) { + str = lock_user(VERIFY_READ, tstr, ret, true); + ret = str ? 0 : -EFAULT; + } + *pstr = str; + return ret; +} + +/* + * TODO: Note that gdb always stores the stat structure big-endian. + * So far, that's ok, as the only two targets using this are also + * big-endian. Until we do something with gdb, also produce the + * same big-endian result from the host. + */ +static int copy_stat_to_user(CPUState *cs, target_ulong addr, + const struct stat *s) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + struct gdb_stat *p; + + if (s->st_dev != (uint32_t)s->st_dev || + s->st_ino != (uint32_t)s->st_ino) { + return -EOVERFLOW; + } + + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); + if (!p) { + return -EFAULT; + } + + p->gdb_st_dev = cpu_to_be32(s->st_dev); + p->gdb_st_ino = cpu_to_be32(s->st_ino); + p->gdb_st_mode = cpu_to_be32(s->st_mode); + p->gdb_st_nlink = cpu_to_be32(s->st_nlink); + p->gdb_st_uid = cpu_to_be32(s->st_uid); + p->gdb_st_gid = cpu_to_be32(s->st_gid); + p->gdb_st_rdev = cpu_to_be32(s->st_rdev); + p->gdb_st_size = cpu_to_be64(s->st_size); +#ifdef _WIN32 + /* Windows stat is missing some fields. */ + p->gdb_st_blksize = 0; + p->gdb_st_blocks = 0; +#else + p->gdb_st_blksize = cpu_to_be64(s->st_blksize); + p->gdb_st_blocks = cpu_to_be64(s->st_blocks); +#endif + p->gdb_st_atime = cpu_to_be32(s->st_atime); + p->gdb_st_mtime = cpu_to_be32(s->st_mtime); + p->gdb_st_ctime = cpu_to_be32(s->st_ctime); + + unlock_user(p, addr, sizeof(struct gdb_stat)); + return 0; +} + +/* + * GDB semihosting syscall implementations. + */ + +static gdb_syscall_complete_cb gdb_open_complete; + +static void gdb_open_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { + int guestfd = alloc_guestfd(); + associate_guestfd(guestfd, ret); + ret = guestfd; + } + gdb_open_complete(cs, ret, err); +} + +static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_open_complete = complete; + gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x", + fname, len, (target_ulong)gdb_flags, (target_ulong)mode); +} + +static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + gdb_do_syscall(complete, "close,%x", (target_ulong)gf->hostfd); +} + +static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + gdb_do_syscall(complete, "read,%x,%x,%x", + (target_ulong)gf->hostfd, buf, len); +} + +static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + gdb_do_syscall(complete, "write,%x,%x,%x", + (target_ulong)gf->hostfd, buf, len); +} + +static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int gdb_whence) +{ + gdb_do_syscall(complete, "lseek,%x,%lx,%x", + (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence); +} + +static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + gdb_do_syscall(complete, "isatty,%x", (target_ulong)gf->hostfd); +} + +static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr); +} + +static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "stat,%s,%x", fname, len, addr); +} + +static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "unlink,%s", fname, len); +} + +static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + int olen, nlen; + + olen = validate_strlen(cs, oname, oname_len); + if (olen < 0) { + complete(cs, -1, -olen); + return; + } + nlen = validate_strlen(cs, nname, nname_len); + if (nlen < 0) { + complete(cs, -1, -nlen); + return; + } + + gdb_do_syscall(complete, "rename,%s,%s", oname, olen, nname, nlen); +} + +static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + int len = validate_strlen(cs, cmd, cmd_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "system,%s", cmd, len); +} + +static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + gdb_do_syscall(complete, "gettimeofday,%x,%x", tv_addr, tz_addr); +} + +/* + * Host semihosting syscall implementations. + */ + +static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *p; + int ret, host_flags; + + ret = validate_lock_user_string(&p, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + if (gdb_flags & GDB_O_WRONLY) { + host_flags = O_WRONLY; + } else if (gdb_flags & GDB_O_RDWR) { + host_flags = O_RDWR; + } else { + host_flags = O_RDONLY; + } + if (gdb_flags & GDB_O_CREAT) { + host_flags |= O_CREAT; + } + if (gdb_flags & GDB_O_TRUNC) { + host_flags |= O_TRUNC; + } + if (gdb_flags & GDB_O_EXCL) { + host_flags |= O_EXCL; + } + + ret = open(p, host_flags, mode); + if (ret < 0) { + complete(cs, -1, errno); + } else { + int guestfd = alloc_guestfd(); + associate_guestfd(guestfd, ret); + complete(cs, guestfd, 0); + } + unlock_user(p, fname, 0); +} + +static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + /* + * Only close the underlying host fd if it's one we opened on behalf + * of the guest in SYS_OPEN. + */ + if (gf->hostfd != STDIN_FILENO && + gf->hostfd != STDOUT_FILENO && + gf->hostfd != STDERR_FILENO && + close(gf->hostfd) < 0) { + complete(cs, -1, errno); + } else { + complete(cs, 0, 0); + } +} + +static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + void *ptr = lock_user(VERIFY_WRITE, buf, len, 0); + ssize_t ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + do { + ret = read(gf->hostfd, ptr, len); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + complete(cs, -1, errno); + unlock_user(ptr, buf, 0); + } else { + complete(cs, ret, 0); + unlock_user(ptr, buf, ret); + } +} + +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; + void *ptr = lock_user(VERIFY_READ, buf, len, 1); + ssize_t ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = write(gf->hostfd, ptr, len); + complete(cs, ret, ret == -1 ? errno : 0); + unlock_user(ptr, buf, 0); +} + +static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int whence) +{ + /* So far, all hosts use the same values. */ + QEMU_BUILD_BUG_ON(GDB_SEEK_SET != SEEK_SET); + QEMU_BUILD_BUG_ON(GDB_SEEK_CUR != SEEK_CUR); + QEMU_BUILD_BUG_ON(GDB_SEEK_END != SEEK_END); + + off_t ret = off; + int err = 0; + + if (ret == off) { + ret = lseek(gf->hostfd, ret, whence); + if (ret == -1) { + err = errno; + } + } else { + ret = -1; + err = EINVAL; + } + complete(cs, ret, err); +} + +static void host_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + int ret = isatty(gf->hostfd); + complete(cs, ret, ret ? 0 : errno); +} + +static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + struct stat buf; + + if (fstat(gf->hostfd, &buf) < 0) { + complete(cs, -1, errno); + } else { + complete(cs, buf.st_size, 0); + } +} + +static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + struct stat buf; + int ret; + + ret = fstat(gf->hostfd, &buf); + if (ret) { + complete(cs, -1, errno); + return; + } + ret = copy_stat_to_user(cs, addr, &buf); + complete(cs, ret ? -1 : 0, ret ? -ret : 0); +} + +static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + struct stat buf; + char *name; + int ret, err; + + ret = validate_lock_user_string(&name, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = stat(name, &buf); + if (ret) { + err = errno; + } else { + ret = copy_stat_to_user(cs, addr, &buf); + err = 0; + if (ret < 0) { + err = -ret; + ret = -1; + } + } + complete(cs, ret, err); + unlock_user(name, fname, 0); +} + +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; + char *p; + int ret; + + ret = validate_lock_user_string(&p, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = remove(p); + complete(cs, ret, ret ? errno : 0); + unlock_user(p, fname, 0); +} + +static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *ostr, *nstr; + int ret; + + ret = validate_lock_user_string(&ostr, cs, oname, oname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + ret = validate_lock_user_string(&nstr, cs, nname, nname_len); + if (ret < 0) { + unlock_user(ostr, oname, 0); + complete(cs, -1, -ret); + return; + } + + ret = rename(ostr, nstr); + complete(cs, ret, ret ? errno : 0); + unlock_user(ostr, oname, 0); + unlock_user(nstr, nname, 0); +} + +static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *p; + int ret; + + ret = validate_lock_user_string(&p, cs, cmd, cmd_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = system(p); + complete(cs, ret, ret == -1 ? errno : 0); + unlock_user(p, cmd, 0); +} + +static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + struct gdb_timeval *p; + int64_t rt; + + /* GDB fails on non-null TZ, so be consistent. */ + if (tz_addr != 0) { + complete(cs, -1, EINVAL); + return; + } + + p = lock_user(VERIFY_WRITE, tv_addr, sizeof(struct gdb_timeval), 0); + if (!p) { + complete(cs, -1, EFAULT); + return; + } + + /* TODO: Like stat, gdb always produces big-endian results; match it. */ + rt = g_get_real_time(); + p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); + p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); + unlock_user(p, tv_addr, sizeof(struct gdb_timeval)); +} + +#ifndef CONFIG_USER_ONLY +static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, GIOCondition cond, int timeout) +{ + /* + * Since this is only used by xtensa in system mode, and stdio is + * handled through GuestFDConsole, and there are no semihosting + * system calls for sockets and the like, that means this descriptor + * must be a normal file. Normal files never block and are thus + * always ready. + */ + complete(cs, cond & (G_IO_IN | G_IO_OUT), 0); +} +#endif + +/* + * Static file semihosting syscall implementations. + */ + +static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + target_ulong rest = gf->staticfile.len - gf->staticfile.off; + void *ptr; + + if (len > rest) { + len = rest; + } + ptr = lock_user(VERIFY_WRITE, buf, len, 0); + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len); + gf->staticfile.off += len; + complete(cs, len, 0); + unlock_user(ptr, buf, len); +} + +static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int gdb_whence) +{ + int64_t ret; + + switch (gdb_whence) { + case GDB_SEEK_SET: + ret = off; + break; + case GDB_SEEK_CUR: + ret = gf->staticfile.off + off; + break; + case GDB_SEEK_END: + ret = gf->staticfile.len + off; + break; + default: + ret = -1; + break; + } + if (ret >= 0 && ret <= gf->staticfile.len) { + gf->staticfile.off = ret; + complete(cs, ret, 0); + } else { + complete(cs, -1, EINVAL); + } +} + +static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + complete(cs, gf->staticfile.len, 0); +} + +/* + * Console semihosting syscall implementations. + */ + +static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *ptr; + int ret; + + ptr = lock_user(VERIFY_WRITE, buf, len, 0); + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = qemu_semihosting_console_read(cs, ptr, len); + complete(cs, ret, 0); + unlock_user(ptr, buf, ret); +} + +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; + char *ptr = lock_user(VERIFY_READ, buf, len, 1); + int ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = qemu_semihosting_console_write(ptr, len); + complete(cs, ret ? ret : -1, ret ? 0 : EIO); + unlock_user(ptr, buf, ret); +} + +static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + static const struct stat tty_buf = { + .st_mode = 020666, /* S_IFCHR, ugo+rw */ + .st_rdev = 5, /* makedev(5, 0) -- linux /dev/tty */ + }; + int ret; + + ret = copy_stat_to_user(cs, addr, &tty_buf); + complete(cs, ret ? -1 : 0, ret ? -ret : 0); +} + +#ifndef CONFIG_USER_ONLY +static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, GIOCondition cond, int timeout) +{ + /* The semihosting console does not support urgent data or errors. */ + cond &= G_IO_IN | G_IO_OUT; + + /* + * Since qemu_semihosting_console_write never blocks, we can + * consider output always ready -- leave G_IO_OUT alone. + * All that remains is to conditionally signal input ready. + * Since output ready causes an immediate return, only block + * for G_IO_IN alone. + * + * TODO: Implement proper timeout. For now, only support + * indefinite wait or immediate poll. + */ + if (cond == G_IO_IN && timeout < 0) { + qemu_semihosting_console_block_until_ready(cs); + /* We returned -- input must be ready. */ + } else if ((cond & G_IO_IN) && !qemu_semihosting_console_ready()) { + cond &= ~G_IO_IN; + } + + complete(cs, cond, 0); +} +#endif + +/* + * Syscall entry points. + */ + +void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + if (use_gdb_syscalls()) { + gdb_open(cs, complete, fname, fname_len, gdb_flags, mode); + } else { + host_open(cs, complete, fname, fname_len, gdb_flags, mode); + } +} + +void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_close(cs, complete, gf); + break; + case GuestFDHost: + host_close(cs, complete, gf); + break; + case GuestFDStatic: + case GuestFDConsole: + complete(cs, 0, 0); + break; + default: + g_assert_not_reached(); + } + dealloc_guestfd(fd); +} + +void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + /* + * Bound length for 64-bit guests on 32-bit hosts, not overlowing ssize_t. + * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad + * idea to do this unconditionally. + */ + if (len > INT32_MAX) { + len = INT32_MAX; + } + switch (gf->type) { + case GuestFDGDB: + gdb_read(cs, complete, gf, buf, len); + break; + case GuestFDHost: + host_read(cs, complete, gf, buf, len); + break; + case GuestFDStatic: + staticfile_read(cs, complete, gf, buf, len); + break; + case GuestFDConsole: + console_read(cs, complete, gf, buf, len); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len) +{ + GuestFD *gf = get_guestfd(fd); + + if (gf) { + semihost_sys_read_gf(cs, complete, gf, buf, len); + } else { + complete(cs, -1, EBADF); + } +} + +void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + /* + * Bound length for 64-bit guests on 32-bit hosts, not overlowing ssize_t. + * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad + * idea to do this unconditionally. + */ + if (len > INT32_MAX) { + len = INT32_MAX; + } + switch (gf->type) { + case GuestFDGDB: + gdb_write(cs, complete, gf, buf, len); + break; + case GuestFDHost: + host_write(cs, complete, gf, buf, len); + break; + case GuestFDConsole: + console_write(cs, complete, gf, buf, len); + break; + case GuestFDStatic: + /* Static files are never open for writing: EBADF. */ + complete(cs, -1, EBADF); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len) +{ + GuestFD *gf = get_guestfd(fd); + + if (gf) { + semihost_sys_write_gf(cs, complete, gf, buf, len); + } else { + complete(cs, -1, EBADF); + } +} + +void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, int64_t off, int gdb_whence) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_lseek(cs, complete, gf, off, gdb_whence); + return; + case GuestFDHost: + host_lseek(cs, complete, gf, off, gdb_whence); + break; + case GuestFDStatic: + staticfile_lseek(cs, complete, gf, off, gdb_whence); + break; + case GuestFDConsole: + complete(cs, -1, ESPIPE); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, 0, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_isatty(cs, complete, gf); + break; + case GuestFDHost: + host_isatty(cs, complete, gf); + break; + case GuestFDStatic: + complete(cs, 0, ENOTTY); + break; + case GuestFDConsole: + complete(cs, 1, 0); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, + gdb_syscall_complete_cb flen_cb, int fd, + target_ulong fstat_addr) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + flen_cb(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_fstat(cs, fstat_cb, gf, fstat_addr); + break; + case GuestFDHost: + host_flen(cs, flen_cb, gf); + break; + case GuestFDStatic: + staticfile_flen(cs, flen_cb, gf); + break; + case GuestFDConsole: + default: + g_assert_not_reached(); + } +} + +void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong addr) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_fstat(cs, complete, gf, addr); + break; + case GuestFDHost: + host_fstat(cs, complete, gf, addr); + break; + case GuestFDConsole: + console_fstat(cs, complete, gf, addr); + break; + case GuestFDStatic: + default: + g_assert_not_reached(); + } +} + +void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + if (use_gdb_syscalls()) { + gdb_stat(cs, complete, fname, fname_len, addr); + } else { + host_stat(cs, complete, fname, fname_len, addr); + } +} + +void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + if (use_gdb_syscalls()) { + gdb_remove(cs, complete, fname, fname_len); + } else { + host_remove(cs, complete, fname, fname_len); + } +} + +void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + if (use_gdb_syscalls()) { + gdb_rename(cs, complete, oname, oname_len, nname, nname_len); + } else { + host_rename(cs, complete, oname, oname_len, nname, nname_len); + } +} + +void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + if (use_gdb_syscalls()) { + gdb_system(cs, complete, cmd, cmd_len); + } else { + host_system(cs, complete, cmd, cmd_len); + } +} + +void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + if (use_gdb_syscalls()) { + gdb_gettimeofday(cs, complete, tv_addr, tz_addr); + } else { + host_gettimeofday(cs, complete, tv_addr, tz_addr); + } +} + +#ifndef CONFIG_USER_ONLY +void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, GIOCondition cond, int timeout) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, G_IO_NVAL, 1); + return; + } + switch (gf->type) { + case GuestFDGDB: + complete(cs, G_IO_NVAL, 1); + break; + case GuestFDHost: + host_poll_one(cs, complete, gf, cond, timeout); + break; + case GuestFDConsole: + console_poll_one(cs, complete, gf, cond, timeout); + break; + case GuestFDStatic: + default: + g_assert_not_reached(); + } +} +#endif diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c new file mode 100644 index 00000000000..80188280697 --- /dev/null +++ b/semihosting/uaccess.c @@ -0,0 +1,91 @@ +/* + * Helper routines to provide target memory access for semihosting + * syscalls in system emulation mode. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ + +#include "qemu/osdep.h" +#include "exec/exec-all.h" +#include "semihosting/softmmu-uaccess.h" + +void *softmmu_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy) +{ + void *p = malloc(len); + if (p && copy) { + if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) { + free(p); + p = NULL; + } + } + return p; +} + +ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr) +{ + int mmu_idx = cpu_mmu_index(env, false); + size_t len = 0; + + while (1) { + size_t left_in_page; + int flags; + void *h; + + /* Find the number of bytes remaining in the page. */ + left_in_page = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); + + flags = probe_access_flags(env, addr, MMU_DATA_LOAD, + mmu_idx, true, &h, 0); + if (flags & TLB_INVALID_MASK) { + return -1; + } + if (flags & TLB_MMIO) { + do { + uint8_t c; + if (cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0)) { + return -1; + } + if (c == 0) { + return len; + } + addr++; + len++; + if (len > INT32_MAX) { + return -1; + } + } while (--left_in_page != 0); + } else { + char *p = memchr(h, 0, left_in_page); + if (p) { + len += p - (char *)h; + return len <= INT32_MAX ? (ssize_t)len : -1; + } + addr += left_in_page; + len += left_in_page; + if (len > INT32_MAX) { + return -1; + } + } + } +} + +char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) +{ + ssize_t len = softmmu_strlen_user(env, addr); + if (len < 0) { + return NULL; + } + return softmmu_lock_user(env, addr, len + 1, true); +} + +void softmmu_unlock_user(CPUArchState *env, void *p, + target_ulong addr, target_ulong len) +{ + if (len) { + cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); + } + free(p); +} diff --git a/softmmu/datadir.c b/softmmu/datadir.c index 160cac999a6..c9237cb5d4a 100644 --- a/softmmu/datadir.c +++ b/softmmu/datadir.c @@ -83,40 +83,22 @@ void qemu_add_data_dir(char *path) data_dir[data_dir_idx++] = path; } -/* - * Find a likely location for support files using the location of the binary. - * When running from the build tree this will be "$bindir/pc-bios". - * Otherwise, this is CONFIG_QEMU_DATADIR (possibly relocated). - * - * The caller must use g_free() to free the returned data when it is - * no longer required. - */ -static char *find_datadir(void) -{ - g_autofree char *dir = NULL; - - dir = g_build_filename(qemu_get_exec_dir(), "pc-bios", NULL); - if (g_file_test(dir, G_FILE_TEST_IS_DIR)) { - return g_steal_pointer(&dir); - } - - return get_relocated_path(CONFIG_QEMU_DATADIR); -} - void qemu_add_default_firmwarepath(void) { - char **dirs; + static const char * const dirs[] = { + CONFIG_QEMU_FIRMWAREPATH + NULL + }; + size_t i; /* add configured firmware directories */ - dirs = g_strsplit(CONFIG_QEMU_FIRMWAREPATH, G_SEARCHPATH_SEPARATOR_S, 0); for (i = 0; dirs[i] != NULL; i++) { qemu_add_data_dir(get_relocated_path(dirs[i])); } - g_strfreev(dirs); /* try to find datadir relative to the executable path */ - qemu_add_data_dir(find_datadir()); + qemu_add_data_dir(get_relocated_path(CONFIG_QEMU_DATADIR)); } void qemu_list_data_dirs(void) diff --git a/softmmu/dirtylimit.c b/softmmu/dirtylimit.c new file mode 100644 index 00000000000..8d98cb7f2c1 --- /dev/null +++ b/softmmu/dirtylimit.c @@ -0,0 +1,601 @@ +/* + * Dirty page rate limit implementation code + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(黄勇) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "sysemu/dirtyrate.h" +#include "sysemu/dirtylimit.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "exec/memory.h" +#include "hw/boards.h" +#include "sysemu/kvm.h" +#include "trace.h" + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ +/* + * Plus or minus vcpu sleep time linearly if dirty + * page rate error value percentage over + * DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT. + * Otherwise, plus or minus a fixed vcpu sleep time. + */ +#define DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT 50 +/* + * Max vcpu sleep time percentage during a cycle + * composed of dirty ring full and sleep time. + */ +#define DIRTYLIMIT_THROTTLE_PCT_MAX 99 + +struct { + VcpuStat stat; + bool running; + QemuThread thread; +} *vcpu_dirty_rate_stat; + +typedef struct VcpuDirtyLimitState { + int cpu_index; + bool enabled; + /* + * Quota dirty page rate, unit is MB/s + * zero if not enabled. + */ + uint64_t quota; +} VcpuDirtyLimitState; + +struct { + VcpuDirtyLimitState *states; + /* Max cpus number configured by user */ + int max_cpus; + /* Number of vcpu under dirtylimit */ + int limited_nvcpu; +} *dirtylimit_state; + +/* protect dirtylimit_state */ +static QemuMutex dirtylimit_mutex; + +/* dirtylimit thread quit if dirtylimit_quit is true */ +static bool dirtylimit_quit; + +static void vcpu_dirty_rate_stat_collect(void) +{ + VcpuStat stat; + int i = 0; + + /* calculate vcpu dirtyrate */ + vcpu_calculate_dirtyrate(DIRTYLIMIT_CALC_TIME_MS, + &stat, + GLOBAL_DIRTY_LIMIT, + false); + + for (i = 0; i < stat.nvcpu; i++) { + vcpu_dirty_rate_stat->stat.rates[i].id = i; + vcpu_dirty_rate_stat->stat.rates[i].dirty_rate = + stat.rates[i].dirty_rate; + } + + free(stat.rates); +} + +static void *vcpu_dirty_rate_stat_thread(void *opaque) +{ + rcu_register_thread(); + + /* start log sync */ + global_dirty_log_change(GLOBAL_DIRTY_LIMIT, true); + + while (qatomic_read(&vcpu_dirty_rate_stat->running)) { + vcpu_dirty_rate_stat_collect(); + if (dirtylimit_in_service()) { + dirtylimit_process(); + } + } + + /* stop log sync */ + global_dirty_log_change(GLOBAL_DIRTY_LIMIT, false); + + rcu_unregister_thread(); + return NULL; +} + +int64_t vcpu_dirty_rate_get(int cpu_index) +{ + DirtyRateVcpu *rates = vcpu_dirty_rate_stat->stat.rates; + return qatomic_read_i64(&rates[cpu_index].dirty_rate); +} + +void vcpu_dirty_rate_stat_start(void) +{ + if (qatomic_read(&vcpu_dirty_rate_stat->running)) { + return; + } + + qatomic_set(&vcpu_dirty_rate_stat->running, 1); + qemu_thread_create(&vcpu_dirty_rate_stat->thread, + "dirtyrate-stat", + vcpu_dirty_rate_stat_thread, + NULL, + QEMU_THREAD_JOINABLE); +} + +void vcpu_dirty_rate_stat_stop(void) +{ + qatomic_set(&vcpu_dirty_rate_stat->running, 0); + dirtylimit_state_unlock(); + qemu_mutex_unlock_iothread(); + qemu_thread_join(&vcpu_dirty_rate_stat->thread); + qemu_mutex_lock_iothread(); + dirtylimit_state_lock(); +} + +void vcpu_dirty_rate_stat_initialize(void) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int max_cpus = ms->smp.max_cpus; + + vcpu_dirty_rate_stat = + g_malloc0(sizeof(*vcpu_dirty_rate_stat)); + + vcpu_dirty_rate_stat->stat.nvcpu = max_cpus; + vcpu_dirty_rate_stat->stat.rates = + g_malloc0(sizeof(DirtyRateVcpu) * max_cpus); + + vcpu_dirty_rate_stat->running = false; +} + +void vcpu_dirty_rate_stat_finalize(void) +{ + free(vcpu_dirty_rate_stat->stat.rates); + vcpu_dirty_rate_stat->stat.rates = NULL; + + free(vcpu_dirty_rate_stat); + vcpu_dirty_rate_stat = NULL; +} + +void dirtylimit_state_lock(void) +{ + qemu_mutex_lock(&dirtylimit_mutex); +} + +void dirtylimit_state_unlock(void) +{ + qemu_mutex_unlock(&dirtylimit_mutex); +} + +static void +__attribute__((__constructor__)) dirtylimit_mutex_init(void) +{ + qemu_mutex_init(&dirtylimit_mutex); +} + +static inline VcpuDirtyLimitState *dirtylimit_vcpu_get_state(int cpu_index) +{ + return &dirtylimit_state->states[cpu_index]; +} + +void dirtylimit_state_initialize(void) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int max_cpus = ms->smp.max_cpus; + int i; + + dirtylimit_state = g_malloc0(sizeof(*dirtylimit_state)); + + dirtylimit_state->states = + g_malloc0(sizeof(VcpuDirtyLimitState) * max_cpus); + + for (i = 0; i < max_cpus; i++) { + dirtylimit_state->states[i].cpu_index = i; + } + + dirtylimit_state->max_cpus = max_cpus; + trace_dirtylimit_state_initialize(max_cpus); +} + +void dirtylimit_state_finalize(void) +{ + free(dirtylimit_state->states); + dirtylimit_state->states = NULL; + + free(dirtylimit_state); + dirtylimit_state = NULL; + + trace_dirtylimit_state_finalize(); +} + +bool dirtylimit_in_service(void) +{ + return !!dirtylimit_state; +} + +bool dirtylimit_vcpu_index_valid(int cpu_index) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + return !(cpu_index < 0 || + cpu_index >= ms->smp.max_cpus); +} + +static inline int64_t dirtylimit_dirty_ring_full_time(uint64_t dirtyrate) +{ + static uint64_t max_dirtyrate; + uint32_t dirty_ring_size = kvm_dirty_ring_size(); + uint64_t dirty_ring_size_meory_MB = + dirty_ring_size * TARGET_PAGE_SIZE >> 20; + + if (max_dirtyrate < dirtyrate) { + max_dirtyrate = dirtyrate; + } + + return dirty_ring_size_meory_MB * 1000000 / max_dirtyrate; +} + +static inline bool dirtylimit_done(uint64_t quota, + uint64_t current) +{ + uint64_t min, max; + + min = MIN(quota, current); + max = MAX(quota, current); + + return ((max - min) <= DIRTYLIMIT_TOLERANCE_RANGE) ? true : false; +} + +static inline bool +dirtylimit_need_linear_adjustment(uint64_t quota, + uint64_t current) +{ + uint64_t min, max; + + min = MIN(quota, current); + max = MAX(quota, current); + + return ((max - min) * 100 / max) > DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT; +} + +static void dirtylimit_set_throttle(CPUState *cpu, + uint64_t quota, + uint64_t current) +{ + int64_t ring_full_time_us = 0; + uint64_t sleep_pct = 0; + uint64_t throttle_us = 0; + + if (current == 0) { + cpu->throttle_us_per_full = 0; + return; + } + + ring_full_time_us = dirtylimit_dirty_ring_full_time(current); + + if (dirtylimit_need_linear_adjustment(quota, current)) { + if (quota < current) { + sleep_pct = (current - quota) * 100 / current; + throttle_us = + ring_full_time_us * sleep_pct / (double)(100 - sleep_pct); + cpu->throttle_us_per_full += throttle_us; + } else { + sleep_pct = (quota - current) * 100 / quota; + throttle_us = + ring_full_time_us * sleep_pct / (double)(100 - sleep_pct); + cpu->throttle_us_per_full -= throttle_us; + } + + trace_dirtylimit_throttle_pct(cpu->cpu_index, + sleep_pct, + throttle_us); + } else { + if (quota < current) { + cpu->throttle_us_per_full += ring_full_time_us / 10; + } else { + cpu->throttle_us_per_full -= ring_full_time_us / 10; + } + } + + /* + * TODO: in the big kvm_dirty_ring_size case (eg: 65536, or other scenario), + * current dirty page rate may never reach the quota, we should stop + * increasing sleep time? + */ + cpu->throttle_us_per_full = MIN(cpu->throttle_us_per_full, + ring_full_time_us * DIRTYLIMIT_THROTTLE_PCT_MAX); + + cpu->throttle_us_per_full = MAX(cpu->throttle_us_per_full, 0); +} + +static void dirtylimit_adjust_throttle(CPUState *cpu) +{ + uint64_t quota = 0; + uint64_t current = 0; + int cpu_index = cpu->cpu_index; + + quota = dirtylimit_vcpu_get_state(cpu_index)->quota; + current = vcpu_dirty_rate_get(cpu_index); + + if (!dirtylimit_done(quota, current)) { + dirtylimit_set_throttle(cpu, quota, current); + } + + return; +} + +void dirtylimit_process(void) +{ + CPUState *cpu; + + if (!qatomic_read(&dirtylimit_quit)) { + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_state_unlock(); + return; + } + + CPU_FOREACH(cpu) { + if (!dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) { + continue; + } + dirtylimit_adjust_throttle(cpu); + } + dirtylimit_state_unlock(); + } +} + +void dirtylimit_change(bool start) +{ + if (start) { + qatomic_set(&dirtylimit_quit, 0); + } else { + qatomic_set(&dirtylimit_quit, 1); + } +} + +void dirtylimit_set_vcpu(int cpu_index, + uint64_t quota, + bool enable) +{ + trace_dirtylimit_set_vcpu(cpu_index, quota); + + if (enable) { + dirtylimit_state->states[cpu_index].quota = quota; + if (!dirtylimit_vcpu_get_state(cpu_index)->enabled) { + dirtylimit_state->limited_nvcpu++; + } + } else { + dirtylimit_state->states[cpu_index].quota = 0; + if (dirtylimit_state->states[cpu_index].enabled) { + dirtylimit_state->limited_nvcpu--; + } + } + + dirtylimit_state->states[cpu_index].enabled = enable; +} + +void dirtylimit_set_all(uint64_t quota, + bool enable) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int max_cpus = ms->smp.max_cpus; + int i; + + for (i = 0; i < max_cpus; i++) { + dirtylimit_set_vcpu(i, quota, enable); + } +} + +void dirtylimit_vcpu_execute(CPUState *cpu) +{ + if (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); + } +} + +static void dirtylimit_init(void) +{ + dirtylimit_state_initialize(); + dirtylimit_change(true); + vcpu_dirty_rate_stat_initialize(); + vcpu_dirty_rate_stat_start(); +} + +static void dirtylimit_cleanup(void) +{ + vcpu_dirty_rate_stat_stop(); + vcpu_dirty_rate_stat_finalize(); + dirtylimit_change(false); + dirtylimit_state_finalize(); +} + +void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index, + int64_t cpu_index, + Error **errp) +{ + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + return; + } + + if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) { + error_setg(errp, "incorrect cpu index specified"); + return; + } + + if (!dirtylimit_in_service()) { + return; + } + + dirtylimit_state_lock(); + + if (has_cpu_index) { + dirtylimit_set_vcpu(cpu_index, 0, false); + } else { + dirtylimit_set_all(0, false); + } + + if (!dirtylimit_state->limited_nvcpu) { + dirtylimit_cleanup(); + } + + dirtylimit_state_unlock(); +} + +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +void qmp_set_vcpu_dirty_limit(bool has_cpu_index, + int64_t cpu_index, + uint64_t dirty_rate, + Error **errp) +{ + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + error_setg(errp, "dirty page limit feature requires KVM with" + " accelerator property 'dirty-ring-size' set'"); + return; + } + + if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) { + error_setg(errp, "incorrect cpu index specified"); + return; + } + + if (!dirty_rate) { + qmp_cancel_vcpu_dirty_limit(has_cpu_index, cpu_index, errp); + return; + } + + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_init(); + } + + if (has_cpu_index) { + dirtylimit_set_vcpu(cpu_index, dirty_rate, true); + } else { + dirtylimit_set_all(dirty_rate, true); + } + + dirtylimit_state_unlock(); +} + +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate"); + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +static struct DirtyLimitInfo *dirtylimit_query_vcpu(int cpu_index) +{ + DirtyLimitInfo *info = NULL; + + info = g_malloc0(sizeof(*info)); + info->cpu_index = cpu_index; + info->limit_rate = dirtylimit_vcpu_get_state(cpu_index)->quota; + info->current_rate = vcpu_dirty_rate_get(cpu_index); + + return info; +} + +static struct DirtyLimitInfoList *dirtylimit_query_all(void) +{ + int i, index; + DirtyLimitInfo *info = NULL; + DirtyLimitInfoList *head = NULL, **tail = &head; + + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_state_unlock(); + return NULL; + } + + for (i = 0; i < dirtylimit_state->max_cpus; i++) { + index = dirtylimit_state->states[i].cpu_index; + if (dirtylimit_vcpu_get_state(index)->enabled) { + info = dirtylimit_query_vcpu(index); + QAPI_LIST_APPEND(tail, info); + } + } + + dirtylimit_state_unlock(); + + return head; +} + +struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) +{ + if (!dirtylimit_in_service()) { + return NULL; + } + + return dirtylimit_query_all(); +} + +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + DirtyLimitInfoList *limit, *head, *info = NULL; + Error *err = NULL; + + if (!dirtylimit_in_service()) { + monitor_printf(mon, "Dirty page limit not enabled!\n"); + return; + } + + info = qmp_query_vcpu_dirty_limit(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + head = info; + for (limit = head; limit != NULL; limit = limit->next) { + monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," + " current rate %"PRIi64 " (MB/s)\n", + limit->value->cpu_index, + limit->value->limit_rate, + limit->value->current_rate); + } + + g_free(info); +} diff --git a/softmmu/main.c b/softmmu/main.c index c00432ff098..f031b486cc2 100644 --- a/softmmu/main.c +++ b/softmmu/main.c @@ -39,9 +39,11 @@ int qemu_main(int argc, char **argv, char **envp) return 0; } +#ifndef AS_LIB #ifndef CONFIG_COCOA int main(int argc, char **argv) { return qemu_main(argc, argv, NULL); } #endif +#endif diff --git a/softmmu/meson.build b/softmmu/meson.build index 8138248661a..3272af1f312 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -4,6 +4,7 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( 'memory.c', 'physmem.c', 'qtest.c', + 'dirtylimit.c', )]) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( diff --git a/softmmu/physmem.c b/softmmu/physmem.c index fb16be57a6c..dc3c3e5f2e7 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -669,7 +669,7 @@ void tcg_iommu_init_notifier_list(CPUState *cpu) /* Called from RCU critical section */ MemoryRegionSection * -address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, +address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, hwaddr *xlat, hwaddr *plen, MemTxAttrs attrs, int *prot) { @@ -678,6 +678,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, IOMMUMemoryRegionClass *imrc; IOMMUTLBEntry iotlb; int iommu_idx; + hwaddr addr = orig_addr; AddressSpaceDispatch *d = qatomic_rcu_read(&cpu->cpu_ases[asidx].memory_dispatch); @@ -722,6 +723,16 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, return section; translate_fail: + /* + * We should be given a page-aligned address -- certainly + * tlb_set_page_with_attrs() does so. The page offset of xlat + * is used to index sections[], and PHYS_SECTION_UNASSIGNED = 0. + * The page portion of xlat will be logged by memory_region_access_valid() + * when this memory access is rejected, so use the original untranslated + * physical address. + */ + assert((orig_addr & ~TARGET_PAGE_MASK) == 0); + *xlat = orig_addr; return &d->map.sections[PHYS_SECTION_UNASSIGNED]; } diff --git a/softmmu/runstate.c b/softmmu/runstate.c index fac7b63259e..168e1b78a05 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -126,6 +126,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH }, { RUN_STATE_COLO, RUN_STATE_RUNNING }, + { RUN_STATE_COLO, RUN_STATE_PRELAUNCH }, { RUN_STATE_COLO, RUN_STATE_SHUTDOWN}, { RUN_STATE_RUNNING, RUN_STATE_DEBUG }, diff --git a/softmmu/trace-events b/softmmu/trace-events index 9c88887b3c6..22606dc27b3 100644 --- a/softmmu/trace-events +++ b/softmmu/trace-events @@ -31,3 +31,10 @@ runstate_set(int current_state, const char *current_state_str, int new_state, co system_wakeup_request(int reason) "reason=%d" qemu_system_shutdown_request(int reason) "reason=%d" qemu_system_powerdown_request(void) "" + +#dirtylimit.c +dirtylimit_state_initialize(int max_cpus) "dirtylimit state initialize: max cpus %d" +dirtylimit_state_finalize(void) +dirtylimit_throttle_pct(int cpu_index, uint64_t pct, int64_t time_us) "CPU[%d] throttle percent: %" PRIu64 ", throttle adjust time %"PRIi64 " us" +dirtylimit_set_vcpu(int cpu_index, uint64_t quota) "CPU[%d] set dirty page rate limit %"PRIu64 +dirtylimit_vcpu_execute(int cpu_index, int64_t sleep_time_us) "CPU[%d] sleep %"PRIi64 " us" diff --git a/softmmu/vl.c b/softmmu/vl.c index 54e920ada1a..aabd82e09a2 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -981,7 +981,8 @@ static void select_vgahw(const MachineClass *machine_class, const char *p) if (vga_interface_available(t) && ti->opt_name) { printf("%-20s %s%s\n", ti->opt_name, ti->name ?: "", - g_str_equal(ti->opt_name, def) ? " (default)" : ""); + (def && g_str_equal(ti->opt_name, def)) ? + " (default)" : ""); } } exit(0); @@ -1521,13 +1522,18 @@ machine_parse_property_opt(QemuOptsList *opts_list, const char *propname, } static const char *pid_file; -static Notifier qemu_unlink_pidfile_notifier; +struct UnlinkPidfileNotifier { + Notifier notifier; + char *pid_file_realpath; +}; +static struct UnlinkPidfileNotifier qemu_unlink_pidfile_notifier; static void qemu_unlink_pidfile(Notifier *n, void *data) { - if (pid_file) { - unlink(pid_file); - } + struct UnlinkPidfileNotifier *upn; + + upn = DO_UPCAST(struct UnlinkPidfileNotifier, notifier, n); + unlink(upn->pid_file_realpath); } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -1917,8 +1923,7 @@ static void qemu_create_late_backends(void) exit(1); /* now chardevs have been created we may have semihosting to connect */ - qemu_semihosting_connect_chardevs(); - qemu_semihosting_console_init(); + qemu_semihosting_chardev_init(); } static void qemu_resolve_machine_memdev(void) @@ -2271,8 +2276,7 @@ static void configure_accelerators(const char *progname) } if (init_failed && !qtest_chrdev) { - AccelClass *ac = ACCEL_GET_CLASS(current_accel()); - error_report("falling back to %s", ac->name); + error_report("falling back to %s", current_accel_name()); } if (icount_enabled() && !tcg_enabled()) { @@ -2432,13 +2436,30 @@ static void qemu_maybe_daemonize(const char *pid_file) os_daemonize(); rcu_disable_atfork(); - if (pid_file && !qemu_write_pidfile(pid_file, &err)) { - error_reportf_err(err, "cannot create PID file: "); - exit(1); - } + if (pid_file) { + char *pid_file_realpath = NULL; - qemu_unlink_pidfile_notifier.notify = qemu_unlink_pidfile; - qemu_add_exit_notifier(&qemu_unlink_pidfile_notifier); + if (!qemu_write_pidfile(pid_file, &err)) { + error_reportf_err(err, "cannot create PID file: "); + exit(1); + } + + pid_file_realpath = g_malloc0(PATH_MAX); + if (!realpath(pid_file, pid_file_realpath)) { + error_report("cannot resolve PID file path: %s: %s", + pid_file, strerror(errno)); + unlink(pid_file); + exit(1); + } + + qemu_unlink_pidfile_notifier = (struct UnlinkPidfileNotifier) { + .notifier = { + .notify = qemu_unlink_pidfile, + }, + .pid_file_realpath = pid_file_realpath, + }; + qemu_add_exit_notifier(&qemu_unlink_pidfile_notifier.notifier); + } } static void qemu_init_displays(void) diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index c104817cddd..7718f6dcda3 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -61,6 +61,7 @@ #include "trace/control.h" static const char *pid_file; +static char *pid_file_realpath; static volatile bool exit_requested = false; void qemu_system_killed(int signal, pid_t pid) @@ -121,6 +122,16 @@ static void help(void) " vhost-user-blk device over file descriptor\n" "\n" #endif /* CONFIG_VHOST_USER_BLK_SERVER */ +#ifdef CONFIG_VDUSE_BLK_EXPORT +" --export [type=]vduse-blk,id=,node-name=\n" +" ,name=[,writable=on|off]\n" +" [,num-queues=][,queue-size=]\n" +" [,logical-block-size=]\n" +" [,serial=]\n" +" export the specified block node as a\n" +" vduse-blk device\n" +"\n" +#endif /* CONFIG_VDUSE_BLK_EXPORT */ " --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" " configure a QMP monitor\n" "\n" @@ -286,7 +297,11 @@ static void process_options(int argc, char *argv[], bool pre_init_pass) } case OPTION_DAEMONIZE: if (os_set_daemonize(true) < 0) { - error_report("--daemonize not supported in this build"); + /* + * --daemonize is parsed before monitor_init_globals_core(), so + * error_report() does not work yet + */ + fprintf(stderr, "--daemonize not supported in this build\n"); exit(EXIT_FAILURE); } break; @@ -349,7 +364,7 @@ static void process_options(int argc, char *argv[], bool pre_init_pass) static void pid_file_cleanup(void) { - unlink(pid_file); + unlink(pid_file_realpath); } static void pid_file_init(void) @@ -365,6 +380,14 @@ static void pid_file_init(void) exit(EXIT_FAILURE); } + pid_file_realpath = g_malloc(PATH_MAX); + if (!realpath(pid_file, pid_file_realpath)) { + error_report("cannot resolve PID file path: %s: %s", + pid_file, strerror(errno)); + unlink(pid_file); + exit(EXIT_FAILURE); + } + atexit(pid_file_cleanup); } diff --git a/stubs/semihost.c b/stubs/semihost.c index 4bf2cf71b9b..f486651afbb 100644 --- a/stubs/semihost.c +++ b/stubs/semihost.c @@ -65,10 +65,6 @@ void semihosting_arg_fallback(const char *file, const char *cmd) { } -void qemu_semihosting_connect_chardevs(void) -{ -} - -void qemu_semihosting_console_init(void) +void qemu_semihosting_chardev_init(void) { } diff --git a/stubs/xen-hw-stub.c b/stubs/xen-hw-stub.c index 15f3921a76b..34a22f2ad72 100644 --- a/stubs/xen-hw-stub.c +++ b/stubs/xen-hw-stub.c @@ -19,8 +19,9 @@ void xen_piix3_set_irq(void *opaque, int irq_num, int level) { } -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) +int xen_set_pci_link_route(uint8_t link, uint8_t irq) { + return -1; } void xen_hvm_inject_msi(uint64_t addr, uint32_t data) diff --git a/subprojects/libvduse/include/atomic.h b/subprojects/libvduse/include/atomic.h new file mode 120000 index 00000000000..8c2be64f7b1 --- /dev/null +++ b/subprojects/libvduse/include/atomic.h @@ -0,0 +1 @@ +../../../include/qemu/atomic.h \ No newline at end of file diff --git a/subprojects/libvduse/include/compiler.h b/subprojects/libvduse/include/compiler.h new file mode 120000 index 00000000000..de7b70697cd --- /dev/null +++ b/subprojects/libvduse/include/compiler.h @@ -0,0 +1 @@ +../../../include/qemu/compiler.h \ No newline at end of file diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c new file mode 100644 index 00000000000..9a2bcec2823 --- /dev/null +++ b/subprojects/libvduse/libvduse.c @@ -0,0 +1,1375 @@ +/* + * VDUSE (vDPA Device in Userspace) library + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * Portions of codes and concepts borrowed from libvhost-user.c, so: + * Copyright IBM, Corp. 2007 + * Copyright (c) 2016 Red Hat, Inc. + * + * Author: + * Xie Yongji + * Anthony Liguori + * Marc-André Lureau + * Victor Kaplansky + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "include/atomic.h" +#include "linux-headers/linux/virtio_ring.h" +#include "linux-headers/linux/virtio_config.h" +#include "linux-headers/linux/vduse.h" +#include "libvduse.h" + +#define VDUSE_VQ_ALIGN 4096 +#define MAX_IOVA_REGIONS 256 + +#define LOG_ALIGNMENT 64 + +/* Round number down to multiple */ +#define ALIGN_DOWN(n, m) ((n) / (m) * (m)) + +/* Round number up to multiple */ +#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m)) + +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +typedef struct VduseDescStateSplit { + uint8_t inflight; + uint8_t padding[5]; + uint16_t next; + uint64_t counter; +} VduseDescStateSplit; + +typedef struct VduseVirtqLogInflight { + uint64_t features; + uint16_t version; + uint16_t desc_num; + uint16_t last_batch_head; + uint16_t used_idx; + VduseDescStateSplit desc[]; +} VduseVirtqLogInflight; + +typedef struct VduseVirtqLog { + VduseVirtqLogInflight inflight; +} VduseVirtqLog; + +typedef struct VduseVirtqInflightDesc { + uint16_t index; + uint64_t counter; +} VduseVirtqInflightDesc; + +typedef struct VduseRing { + unsigned int num; + uint64_t desc_addr; + uint64_t avail_addr; + uint64_t used_addr; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +} VduseRing; + +struct VduseVirtq { + VduseRing vring; + uint16_t last_avail_idx; + uint16_t shadow_avail_idx; + uint16_t used_idx; + uint16_t signalled_used; + bool signalled_used_valid; + int index; + int inuse; + bool ready; + int fd; + VduseDev *dev; + VduseVirtqInflightDesc *resubmit_list; + uint16_t resubmit_num; + uint64_t counter; + VduseVirtqLog *log; +}; + +typedef struct VduseIovaRegion { + uint64_t iova; + uint64_t size; + uint64_t mmap_offset; + uint64_t mmap_addr; +} VduseIovaRegion; + +struct VduseDev { + VduseVirtq *vqs; + VduseIovaRegion regions[MAX_IOVA_REGIONS]; + int num_regions; + char *name; + uint32_t device_id; + uint32_t vendor_id; + uint16_t num_queues; + uint16_t queue_size; + uint64_t features; + const VduseOps *ops; + int fd; + int ctrl_fd; + void *priv; + void *log; +}; + +static inline size_t vduse_vq_log_size(uint16_t queue_size) +{ + return ALIGN_UP(sizeof(VduseDescStateSplit) * queue_size + + sizeof(VduseVirtqLogInflight), LOG_ALIGNMENT); +} + +static void *vduse_log_get(const char *filename, size_t size) +{ + void *ptr = MAP_FAILED; + int fd; + + fd = open(filename, O_RDWR | O_CREAT, 0600); + if (fd == -1) { + return MAP_FAILED; + } + + if (ftruncate(fd, size) == -1) { + goto out; + } + + ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + +out: + close(fd); + return ptr; +} + +static inline bool has_feature(uint64_t features, unsigned int fbit) +{ + assert(fbit < 64); + return !!(features & (1ULL << fbit)); +} + +static inline bool vduse_dev_has_feature(VduseDev *dev, unsigned int fbit) +{ + return has_feature(dev->features, fbit); +} + +uint64_t vduse_get_virtio_features(void) +{ + return (1ULL << VIRTIO_F_IOMMU_PLATFORM) | + (1ULL << VIRTIO_F_VERSION_1) | + (1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | + (1ULL << VIRTIO_RING_F_EVENT_IDX) | + (1ULL << VIRTIO_RING_F_INDIRECT_DESC); +} + +VduseDev *vduse_queue_get_dev(VduseVirtq *vq) +{ + return vq->dev; +} + +int vduse_queue_get_fd(VduseVirtq *vq) +{ + return vq->fd; +} + +void *vduse_dev_get_priv(VduseDev *dev) +{ + return dev->priv; +} + +VduseVirtq *vduse_dev_get_queue(VduseDev *dev, int index) +{ + return &dev->vqs[index]; +} + +int vduse_dev_get_fd(VduseDev *dev) +{ + return dev->fd; +} + +static int vduse_inject_irq(VduseDev *dev, int index) +{ + return ioctl(dev->fd, VDUSE_VQ_INJECT_IRQ, &index); +} + +static int inflight_desc_compare(const void *a, const void *b) +{ + VduseVirtqInflightDesc *desc0 = (VduseVirtqInflightDesc *)a, + *desc1 = (VduseVirtqInflightDesc *)b; + + if (desc1->counter > desc0->counter && + (desc1->counter - desc0->counter) < VIRTQUEUE_MAX_SIZE * 2) { + return 1; + } + + return -1; +} + +static int vduse_queue_check_inflights(VduseVirtq *vq) +{ + int i = 0; + VduseDev *dev = vq->dev; + + vq->used_idx = le16toh(vq->vring.used->idx); + vq->resubmit_num = 0; + vq->resubmit_list = NULL; + vq->counter = 0; + + if (unlikely(vq->log->inflight.used_idx != vq->used_idx)) { + if (vq->log->inflight.last_batch_head > VIRTQUEUE_MAX_SIZE) { + return -1; + } + + vq->log->inflight.desc[vq->log->inflight.last_batch_head].inflight = 0; + + barrier(); + + vq->log->inflight.used_idx = vq->used_idx; + } + + for (i = 0; i < vq->log->inflight.desc_num; i++) { + if (vq->log->inflight.desc[i].inflight == 1) { + vq->inuse++; + } + } + + vq->shadow_avail_idx = vq->last_avail_idx = vq->inuse + vq->used_idx; + + if (vq->inuse) { + vq->resubmit_list = calloc(vq->inuse, sizeof(VduseVirtqInflightDesc)); + if (!vq->resubmit_list) { + return -1; + } + + for (i = 0; i < vq->log->inflight.desc_num; i++) { + if (vq->log->inflight.desc[i].inflight) { + vq->resubmit_list[vq->resubmit_num].index = i; + vq->resubmit_list[vq->resubmit_num].counter = + vq->log->inflight.desc[i].counter; + vq->resubmit_num++; + } + } + + if (vq->resubmit_num > 1) { + qsort(vq->resubmit_list, vq->resubmit_num, + sizeof(VduseVirtqInflightDesc), inflight_desc_compare); + } + vq->counter = vq->resubmit_list[0].counter + 1; + } + + vduse_inject_irq(dev, vq->index); + + return 0; +} + +static int vduse_queue_inflight_get(VduseVirtq *vq, int desc_idx) +{ + vq->log->inflight.desc[desc_idx].counter = vq->counter++; + + barrier(); + + vq->log->inflight.desc[desc_idx].inflight = 1; + + return 0; +} + +static int vduse_queue_inflight_pre_put(VduseVirtq *vq, int desc_idx) +{ + vq->log->inflight.last_batch_head = desc_idx; + + return 0; +} + +static int vduse_queue_inflight_post_put(VduseVirtq *vq, int desc_idx) +{ + vq->log->inflight.desc[desc_idx].inflight = 0; + + barrier(); + + vq->log->inflight.used_idx = vq->used_idx; + + return 0; +} + +static void vduse_iova_remove_region(VduseDev *dev, uint64_t start, + uint64_t last) +{ + int i; + + if (last == start) { + return; + } + + for (i = 0; i < MAX_IOVA_REGIONS; i++) { + if (!dev->regions[i].mmap_addr) { + continue; + } + + if (start <= dev->regions[i].iova && + last >= (dev->regions[i].iova + dev->regions[i].size - 1)) { + munmap((void *)(uintptr_t)dev->regions[i].mmap_addr, + dev->regions[i].mmap_offset + dev->regions[i].size); + dev->regions[i].mmap_addr = 0; + dev->num_regions--; + } + } +} + +static int vduse_iova_add_region(VduseDev *dev, int fd, + uint64_t offset, uint64_t start, + uint64_t last, int prot) +{ + int i; + uint64_t size = last - start + 1; + void *mmap_addr = mmap(0, size + offset, prot, MAP_SHARED, fd, 0); + + if (mmap_addr == MAP_FAILED) { + close(fd); + return -EINVAL; + } + + for (i = 0; i < MAX_IOVA_REGIONS; i++) { + if (!dev->regions[i].mmap_addr) { + dev->regions[i].mmap_addr = (uint64_t)(uintptr_t)mmap_addr; + dev->regions[i].mmap_offset = offset; + dev->regions[i].iova = start; + dev->regions[i].size = size; + dev->num_regions++; + break; + } + } + assert(i < MAX_IOVA_REGIONS); + close(fd); + + return 0; +} + +static int perm_to_prot(uint8_t perm) +{ + int prot = 0; + + switch (perm) { + case VDUSE_ACCESS_WO: + prot |= PROT_WRITE; + break; + case VDUSE_ACCESS_RO: + prot |= PROT_READ; + break; + case VDUSE_ACCESS_RW: + prot |= PROT_READ | PROT_WRITE; + break; + default: + break; + } + + return prot; +} + +static inline void *iova_to_va(VduseDev *dev, uint64_t *plen, uint64_t iova) +{ + int i, ret; + struct vduse_iotlb_entry entry; + + for (i = 0; i < MAX_IOVA_REGIONS; i++) { + VduseIovaRegion *r = &dev->regions[i]; + + if (!r->mmap_addr) { + continue; + } + + if ((iova >= r->iova) && (iova < (r->iova + r->size))) { + if ((iova + *plen) > (r->iova + r->size)) { + *plen = r->iova + r->size - iova; + } + return (void *)(uintptr_t)(iova - r->iova + + r->mmap_addr + r->mmap_offset); + } + } + + entry.start = iova; + entry.last = iova + 1; + ret = ioctl(dev->fd, VDUSE_IOTLB_GET_FD, &entry); + if (ret < 0) { + return NULL; + } + + if (!vduse_iova_add_region(dev, ret, entry.offset, entry.start, + entry.last, perm_to_prot(entry.perm))) { + return iova_to_va(dev, plen, iova); + } + + return NULL; +} + +static inline uint16_t vring_avail_flags(VduseVirtq *vq) +{ + return le16toh(vq->vring.avail->flags); +} + +static inline uint16_t vring_avail_idx(VduseVirtq *vq) +{ + vq->shadow_avail_idx = le16toh(vq->vring.avail->idx); + + return vq->shadow_avail_idx; +} + +static inline uint16_t vring_avail_ring(VduseVirtq *vq, int i) +{ + return le16toh(vq->vring.avail->ring[i]); +} + +static inline uint16_t vring_get_used_event(VduseVirtq *vq) +{ + return vring_avail_ring(vq, vq->vring.num); +} + +static bool vduse_queue_get_head(VduseVirtq *vq, unsigned int idx, + unsigned int *head) +{ + /* + * Grab the next descriptor number they're advertising, and increment + * the index we've seen. + */ + *head = vring_avail_ring(vq, idx % vq->vring.num); + + /* If their number is silly, that's a fatal mistake. */ + if (*head >= vq->vring.num) { + fprintf(stderr, "Guest says index %u is available\n", *head); + return false; + } + + return true; +} + +static int +vduse_queue_read_indirect_desc(VduseDev *dev, struct vring_desc *desc, + uint64_t addr, size_t len) +{ + struct vring_desc *ori_desc; + uint64_t read_len; + + if (len > (VIRTQUEUE_MAX_SIZE * sizeof(struct vring_desc))) { + return -1; + } + + if (len == 0) { + return -1; + } + + while (len) { + read_len = len; + ori_desc = iova_to_va(dev, &read_len, addr); + if (!ori_desc) { + return -1; + } + + memcpy(desc, ori_desc, read_len); + len -= read_len; + addr += read_len; + desc += read_len; + } + + return 0; +} + +enum { + VIRTQUEUE_READ_DESC_ERROR = -1, + VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */ + VIRTQUEUE_READ_DESC_MORE = 1, /* more buffers in chain */ +}; + +static int vduse_queue_read_next_desc(struct vring_desc *desc, int i, + unsigned int max, unsigned int *next) +{ + /* If this descriptor says it doesn't chain, we're done. */ + if (!(le16toh(desc[i].flags) & VRING_DESC_F_NEXT)) { + return VIRTQUEUE_READ_DESC_DONE; + } + + /* Check they're not leading us off end of descriptors. */ + *next = desc[i].next; + /* Make sure compiler knows to grab that: we don't want it changing! */ + smp_wmb(); + + if (*next >= max) { + fprintf(stderr, "Desc next is %u\n", *next); + return VIRTQUEUE_READ_DESC_ERROR; + } + + return VIRTQUEUE_READ_DESC_MORE; +} + +/* + * Fetch avail_idx from VQ memory only when we really need to know if + * guest has added some buffers. + */ +static bool vduse_queue_empty(VduseVirtq *vq) +{ + if (unlikely(!vq->vring.avail)) { + return true; + } + + if (vq->shadow_avail_idx != vq->last_avail_idx) { + return false; + } + + return vring_avail_idx(vq) == vq->last_avail_idx; +} + +static bool vduse_queue_should_notify(VduseVirtq *vq) +{ + VduseDev *dev = vq->dev; + uint16_t old, new; + bool v; + + /* We need to expose used array entries before checking used event. */ + smp_mb(); + + /* Always notify when queue is empty (when feature acknowledge) */ + if (vduse_dev_has_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY) && + !vq->inuse && vduse_queue_empty(vq)) { + return true; + } + + if (!vduse_dev_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { + return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); + } + + v = vq->signalled_used_valid; + vq->signalled_used_valid = true; + old = vq->signalled_used; + new = vq->signalled_used = vq->used_idx; + return !v || vring_need_event(vring_get_used_event(vq), new, old); +} + +void vduse_queue_notify(VduseVirtq *vq) +{ + VduseDev *dev = vq->dev; + + if (unlikely(!vq->vring.avail)) { + return; + } + + if (!vduse_queue_should_notify(vq)) { + return; + } + + if (vduse_inject_irq(dev, vq->index) < 0) { + fprintf(stderr, "Error inject irq for vq %d: %s\n", + vq->index, strerror(errno)); + } +} + +static inline void vring_set_avail_event(VduseVirtq *vq, uint16_t val) +{ + *((uint16_t *)&vq->vring.used->ring[vq->vring.num]) = htole16(val); +} + +static bool vduse_queue_map_single_desc(VduseVirtq *vq, unsigned int *p_num_sg, + struct iovec *iov, unsigned int max_num_sg, + bool is_write, uint64_t pa, size_t sz) +{ + unsigned num_sg = *p_num_sg; + VduseDev *dev = vq->dev; + + assert(num_sg <= max_num_sg); + + if (!sz) { + fprintf(stderr, "virtio: zero sized buffers are not allowed\n"); + return false; + } + + while (sz) { + uint64_t len = sz; + + if (num_sg == max_num_sg) { + fprintf(stderr, + "virtio: too many descriptors in indirect table\n"); + return false; + } + + iov[num_sg].iov_base = iova_to_va(dev, &len, pa); + if (iov[num_sg].iov_base == NULL) { + fprintf(stderr, "virtio: invalid address for buffers\n"); + return false; + } + iov[num_sg++].iov_len = len; + sz -= len; + pa += len; + } + + *p_num_sg = num_sg; + return true; +} + +static void *vduse_queue_alloc_element(size_t sz, unsigned out_num, + unsigned in_num) +{ + VduseVirtqElement *elem; + size_t in_sg_ofs = ALIGN_UP(sz, __alignof__(elem->in_sg[0])); + size_t out_sg_ofs = in_sg_ofs + in_num * sizeof(elem->in_sg[0]); + size_t out_sg_end = out_sg_ofs + out_num * sizeof(elem->out_sg[0]); + + assert(sz >= sizeof(VduseVirtqElement)); + elem = malloc(out_sg_end); + if (!elem) { + return NULL; + } + elem->out_num = out_num; + elem->in_num = in_num; + elem->in_sg = (void *)elem + in_sg_ofs; + elem->out_sg = (void *)elem + out_sg_ofs; + return elem; +} + +static void *vduse_queue_map_desc(VduseVirtq *vq, unsigned int idx, size_t sz) +{ + struct vring_desc *desc = vq->vring.desc; + VduseDev *dev = vq->dev; + uint64_t desc_addr, read_len; + unsigned int desc_len; + unsigned int max = vq->vring.num; + unsigned int i = idx; + VduseVirtqElement *elem; + struct iovec iov[VIRTQUEUE_MAX_SIZE]; + struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE]; + unsigned int out_num = 0, in_num = 0; + int rc; + + if (le16toh(desc[i].flags) & VRING_DESC_F_INDIRECT) { + if (le32toh(desc[i].len) % sizeof(struct vring_desc)) { + fprintf(stderr, "Invalid size for indirect buffer table\n"); + return NULL; + } + + /* loop over the indirect descriptor table */ + desc_addr = le64toh(desc[i].addr); + desc_len = le32toh(desc[i].len); + max = desc_len / sizeof(struct vring_desc); + read_len = desc_len; + desc = iova_to_va(dev, &read_len, desc_addr); + if (unlikely(desc && read_len != desc_len)) { + /* Failed to use zero copy */ + desc = NULL; + if (!vduse_queue_read_indirect_desc(dev, desc_buf, + desc_addr, + desc_len)) { + desc = desc_buf; + } + } + if (!desc) { + fprintf(stderr, "Invalid indirect buffer table\n"); + return NULL; + } + i = 0; + } + + /* Collect all the descriptors */ + do { + if (le16toh(desc[i].flags) & VRING_DESC_F_WRITE) { + if (!vduse_queue_map_single_desc(vq, &in_num, iov + out_num, + VIRTQUEUE_MAX_SIZE - out_num, + true, le64toh(desc[i].addr), + le32toh(desc[i].len))) { + return NULL; + } + } else { + if (in_num) { + fprintf(stderr, "Incorrect order for descriptors\n"); + return NULL; + } + if (!vduse_queue_map_single_desc(vq, &out_num, iov, + VIRTQUEUE_MAX_SIZE, false, + le64toh(desc[i].addr), + le32toh(desc[i].len))) { + return NULL; + } + } + + /* If we've got too many, that implies a descriptor loop. */ + if ((in_num + out_num) > max) { + fprintf(stderr, "Looped descriptor\n"); + return NULL; + } + rc = vduse_queue_read_next_desc(desc, i, max, &i); + } while (rc == VIRTQUEUE_READ_DESC_MORE); + + if (rc == VIRTQUEUE_READ_DESC_ERROR) { + fprintf(stderr, "read descriptor error\n"); + return NULL; + } + + /* Now copy what we have collected and mapped */ + elem = vduse_queue_alloc_element(sz, out_num, in_num); + if (!elem) { + fprintf(stderr, "read descriptor error\n"); + return NULL; + } + elem->index = idx; + for (i = 0; i < out_num; i++) { + elem->out_sg[i] = iov[i]; + } + for (i = 0; i < in_num; i++) { + elem->in_sg[i] = iov[out_num + i]; + } + + return elem; +} + +void *vduse_queue_pop(VduseVirtq *vq, size_t sz) +{ + unsigned int head; + VduseVirtqElement *elem; + VduseDev *dev = vq->dev; + int i; + + if (unlikely(!vq->vring.avail)) { + return NULL; + } + + if (unlikely(vq->resubmit_list && vq->resubmit_num > 0)) { + i = (--vq->resubmit_num); + elem = vduse_queue_map_desc(vq, vq->resubmit_list[i].index, sz); + + if (!vq->resubmit_num) { + free(vq->resubmit_list); + vq->resubmit_list = NULL; + } + + return elem; + } + + if (vduse_queue_empty(vq)) { + return NULL; + } + /* Needed after virtio_queue_empty() */ + smp_rmb(); + + if (vq->inuse >= vq->vring.num) { + fprintf(stderr, "Virtqueue size exceeded: %d\n", vq->inuse); + return NULL; + } + + if (!vduse_queue_get_head(vq, vq->last_avail_idx++, &head)) { + return NULL; + } + + if (vduse_dev_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { + vring_set_avail_event(vq, vq->last_avail_idx); + } + + elem = vduse_queue_map_desc(vq, head, sz); + + if (!elem) { + return NULL; + } + + vq->inuse++; + + vduse_queue_inflight_get(vq, head); + + return elem; +} + +static inline void vring_used_write(VduseVirtq *vq, + struct vring_used_elem *uelem, int i) +{ + struct vring_used *used = vq->vring.used; + + used->ring[i] = *uelem; +} + +static void vduse_queue_fill(VduseVirtq *vq, const VduseVirtqElement *elem, + unsigned int len, unsigned int idx) +{ + struct vring_used_elem uelem; + + if (unlikely(!vq->vring.used)) { + return; + } + + idx = (idx + vq->used_idx) % vq->vring.num; + + uelem.id = htole32(elem->index); + uelem.len = htole32(len); + vring_used_write(vq, &uelem, idx); +} + +static inline void vring_used_idx_set(VduseVirtq *vq, uint16_t val) +{ + vq->vring.used->idx = htole16(val); + vq->used_idx = val; +} + +static void vduse_queue_flush(VduseVirtq *vq, unsigned int count) +{ + uint16_t old, new; + + if (unlikely(!vq->vring.used)) { + return; + } + + /* Make sure buffer is written before we update index. */ + smp_wmb(); + + old = vq->used_idx; + new = old + count; + vring_used_idx_set(vq, new); + vq->inuse -= count; + if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) { + vq->signalled_used_valid = false; + } +} + +void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem, + unsigned int len) +{ + vduse_queue_fill(vq, elem, len, 0); + vduse_queue_inflight_pre_put(vq, elem->index); + vduse_queue_flush(vq, 1); + vduse_queue_inflight_post_put(vq, elem->index); +} + +static int vduse_queue_update_vring(VduseVirtq *vq, uint64_t desc_addr, + uint64_t avail_addr, uint64_t used_addr) +{ + struct VduseDev *dev = vq->dev; + uint64_t len; + + len = sizeof(struct vring_desc); + vq->vring.desc = iova_to_va(dev, &len, desc_addr); + if (len != sizeof(struct vring_desc)) { + return -EINVAL; + } + + len = sizeof(struct vring_avail); + vq->vring.avail = iova_to_va(dev, &len, avail_addr); + if (len != sizeof(struct vring_avail)) { + return -EINVAL; + } + + len = sizeof(struct vring_used); + vq->vring.used = iova_to_va(dev, &len, used_addr); + if (len != sizeof(struct vring_used)) { + return -EINVAL; + } + + if (!vq->vring.desc || !vq->vring.avail || !vq->vring.used) { + fprintf(stderr, "Failed to get vq[%d] iova mapping\n", vq->index); + return -EINVAL; + } + + return 0; +} + +static void vduse_queue_enable(VduseVirtq *vq) +{ + struct VduseDev *dev = vq->dev; + struct vduse_vq_info vq_info; + struct vduse_vq_eventfd vq_eventfd; + int fd; + + vq_info.index = vq->index; + if (ioctl(dev->fd, VDUSE_VQ_GET_INFO, &vq_info)) { + fprintf(stderr, "Failed to get vq[%d] info: %s\n", + vq->index, strerror(errno)); + return; + } + + if (!vq_info.ready) { + return; + } + + vq->vring.num = vq_info.num; + vq->vring.desc_addr = vq_info.desc_addr; + vq->vring.avail_addr = vq_info.driver_addr; + vq->vring.used_addr = vq_info.device_addr; + + if (vduse_queue_update_vring(vq, vq_info.desc_addr, + vq_info.driver_addr, vq_info.device_addr)) { + fprintf(stderr, "Failed to update vring for vq[%d]\n", vq->index); + return; + } + + fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "Failed to init eventfd for vq[%d]\n", vq->index); + return; + } + + vq_eventfd.index = vq->index; + vq_eventfd.fd = fd; + if (ioctl(dev->fd, VDUSE_VQ_SETUP_KICKFD, &vq_eventfd)) { + fprintf(stderr, "Failed to setup kick fd for vq[%d]\n", vq->index); + close(fd); + return; + } + + vq->fd = fd; + vq->signalled_used_valid = false; + vq->ready = true; + + if (vduse_queue_check_inflights(vq)) { + fprintf(stderr, "Failed to check inflights for vq[%d]\n", vq->index); + close(fd); + return; + } + + dev->ops->enable_queue(dev, vq); +} + +static void vduse_queue_disable(VduseVirtq *vq) +{ + struct VduseDev *dev = vq->dev; + struct vduse_vq_eventfd eventfd; + + if (!vq->ready) { + return; + } + + dev->ops->disable_queue(dev, vq); + + eventfd.index = vq->index; + eventfd.fd = VDUSE_EVENTFD_DEASSIGN; + ioctl(dev->fd, VDUSE_VQ_SETUP_KICKFD, &eventfd); + close(vq->fd); + + assert(vq->inuse == 0); + + vq->vring.num = 0; + vq->vring.desc_addr = 0; + vq->vring.avail_addr = 0; + vq->vring.used_addr = 0; + vq->vring.desc = 0; + vq->vring.avail = 0; + vq->vring.used = 0; + vq->ready = false; + vq->fd = -1; +} + +static void vduse_dev_start_dataplane(VduseDev *dev) +{ + int i; + + if (ioctl(dev->fd, VDUSE_DEV_GET_FEATURES, &dev->features)) { + fprintf(stderr, "Failed to get features: %s\n", strerror(errno)); + return; + } + assert(vduse_dev_has_feature(dev, VIRTIO_F_VERSION_1)); + + for (i = 0; i < dev->num_queues; i++) { + vduse_queue_enable(&dev->vqs[i]); + } +} + +static void vduse_dev_stop_dataplane(VduseDev *dev) +{ + size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE); + int i; + + for (i = 0; i < dev->num_queues; i++) { + vduse_queue_disable(&dev->vqs[i]); + } + if (dev->log) { + memset(dev->log, 0, log_size); + } + dev->features = 0; + vduse_iova_remove_region(dev, 0, ULONG_MAX); +} + +int vduse_dev_handler(VduseDev *dev) +{ + struct vduse_dev_request req; + struct vduse_dev_response resp = { 0 }; + VduseVirtq *vq; + int i, ret; + + ret = read(dev->fd, &req, sizeof(req)); + if (ret != sizeof(req)) { + fprintf(stderr, "Read request error [%d]: %s\n", + ret, strerror(errno)); + return -errno; + } + resp.request_id = req.request_id; + + switch (req.type) { + case VDUSE_GET_VQ_STATE: + vq = &dev->vqs[req.vq_state.index]; + resp.vq_state.split.avail_index = vq->last_avail_idx; + resp.result = VDUSE_REQ_RESULT_OK; + break; + case VDUSE_SET_STATUS: + if (req.s.status & VIRTIO_CONFIG_S_DRIVER_OK) { + vduse_dev_start_dataplane(dev); + } else if (req.s.status == 0) { + vduse_dev_stop_dataplane(dev); + } + resp.result = VDUSE_REQ_RESULT_OK; + break; + case VDUSE_UPDATE_IOTLB: + /* The iova will be updated by iova_to_va() later, so just remove it */ + vduse_iova_remove_region(dev, req.iova.start, req.iova.last); + for (i = 0; i < dev->num_queues; i++) { + VduseVirtq *vq = &dev->vqs[i]; + if (vq->ready) { + if (vduse_queue_update_vring(vq, vq->vring.desc_addr, + vq->vring.avail_addr, + vq->vring.used_addr)) { + fprintf(stderr, "Failed to update vring for vq[%d]\n", + vq->index); + } + } + } + resp.result = VDUSE_REQ_RESULT_OK; + break; + default: + resp.result = VDUSE_REQ_RESULT_FAILED; + break; + } + + ret = write(dev->fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) { + fprintf(stderr, "Write request %d error [%d]: %s\n", + req.type, ret, strerror(errno)); + return -errno; + } + return 0; +} + +int vduse_dev_update_config(VduseDev *dev, uint32_t size, + uint32_t offset, char *buffer) +{ + int ret; + struct vduse_config_data *data; + + data = malloc(offsetof(struct vduse_config_data, buffer) + size); + if (!data) { + return -ENOMEM; + } + + data->offset = offset; + data->length = size; + memcpy(data->buffer, buffer, size); + + ret = ioctl(dev->fd, VDUSE_DEV_SET_CONFIG, data); + free(data); + + if (ret) { + return -errno; + } + + if (ioctl(dev->fd, VDUSE_DEV_INJECT_CONFIG_IRQ)) { + return -errno; + } + + return 0; +} + +int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size) +{ + VduseVirtq *vq = &dev->vqs[index]; + struct vduse_vq_config vq_config = { 0 }; + + if (max_size > VIRTQUEUE_MAX_SIZE) { + return -EINVAL; + } + + vq_config.index = vq->index; + vq_config.max_size = max_size; + + if (ioctl(dev->fd, VDUSE_VQ_SETUP, &vq_config)) { + return -errno; + } + + vduse_queue_enable(vq); + + return 0; +} + +int vduse_set_reconnect_log_file(VduseDev *dev, const char *filename) +{ + + size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE); + void *log; + int i; + + dev->log = log = vduse_log_get(filename, log_size); + if (log == MAP_FAILED) { + fprintf(stderr, "Failed to get vduse log\n"); + return -EINVAL; + } + + for (i = 0; i < dev->num_queues; i++) { + dev->vqs[i].log = log; + dev->vqs[i].log->inflight.desc_num = VIRTQUEUE_MAX_SIZE; + log = (void *)((char *)log + vduse_vq_log_size(VIRTQUEUE_MAX_SIZE)); + } + + return 0; +} + +static int vduse_dev_init_vqs(VduseDev *dev, uint16_t num_queues) +{ + VduseVirtq *vqs; + int i; + + vqs = calloc(sizeof(VduseVirtq), num_queues); + if (!vqs) { + return -ENOMEM; + } + + for (i = 0; i < num_queues; i++) { + vqs[i].index = i; + vqs[i].dev = dev; + vqs[i].fd = -1; + } + dev->vqs = vqs; + + return 0; +} + +static int vduse_dev_init(VduseDev *dev, const char *name, + uint16_t num_queues, const VduseOps *ops, + void *priv) +{ + char *dev_path, *dev_name; + int ret, fd; + + dev_path = malloc(strlen(name) + strlen("/dev/vduse/") + 1); + if (!dev_path) { + return -ENOMEM; + } + sprintf(dev_path, "/dev/vduse/%s", name); + + fd = open(dev_path, O_RDWR); + free(dev_path); + if (fd < 0) { + fprintf(stderr, "Failed to open vduse dev %s: %s\n", + name, strerror(errno)); + return -errno; + } + + if (ioctl(fd, VDUSE_DEV_GET_FEATURES, &dev->features)) { + fprintf(stderr, "Failed to get features: %s\n", strerror(errno)); + close(fd); + return -errno; + } + + dev_name = strdup(name); + if (!dev_name) { + close(fd); + return -ENOMEM; + } + + ret = vduse_dev_init_vqs(dev, num_queues); + if (ret) { + free(dev_name); + close(fd); + return ret; + } + + dev->name = dev_name; + dev->num_queues = num_queues; + dev->fd = fd; + dev->ops = ops; + dev->priv = priv; + + return 0; +} + +static inline bool vduse_name_is_valid(const char *name) +{ + return strlen(name) >= VDUSE_NAME_MAX || strstr(name, ".."); +} + +VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues, + const VduseOps *ops, void *priv) +{ + VduseDev *dev; + int ret; + + if (!ops || !ops->enable_queue || !ops->disable_queue) { + fprintf(stderr, "Invalid parameter for vduse\n"); + return NULL; + } + + dev = calloc(sizeof(VduseDev), 1); + if (!dev) { + fprintf(stderr, "Failed to allocate vduse device\n"); + return NULL; + } + + if (ioctl(fd, VDUSE_DEV_GET_FEATURES, &dev->features)) { + fprintf(stderr, "Failed to get features: %s\n", strerror(errno)); + free(dev); + return NULL; + } + + ret = vduse_dev_init_vqs(dev, num_queues); + if (ret) { + fprintf(stderr, "Failed to init vqs\n"); + free(dev); + return NULL; + } + + dev->num_queues = num_queues; + dev->fd = fd; + dev->ops = ops; + dev->priv = priv; + + return dev; +} + +VduseDev *vduse_dev_create_by_name(const char *name, uint16_t num_queues, + const VduseOps *ops, void *priv) +{ + VduseDev *dev; + int ret; + + if (!name || vduse_name_is_valid(name) || !ops || + !ops->enable_queue || !ops->disable_queue) { + fprintf(stderr, "Invalid parameter for vduse\n"); + return NULL; + } + + dev = calloc(sizeof(VduseDev), 1); + if (!dev) { + fprintf(stderr, "Failed to allocate vduse device\n"); + return NULL; + } + + ret = vduse_dev_init(dev, name, num_queues, ops, priv); + if (ret < 0) { + fprintf(stderr, "Failed to init vduse device %s: %s\n", + name, strerror(ret)); + free(dev); + return NULL; + } + + return dev; +} + +VduseDev *vduse_dev_create(const char *name, uint32_t device_id, + uint32_t vendor_id, uint64_t features, + uint16_t num_queues, uint32_t config_size, + char *config, const VduseOps *ops, void *priv) +{ + VduseDev *dev; + int ret, ctrl_fd; + uint64_t version; + struct vduse_dev_config *dev_config; + size_t size = offsetof(struct vduse_dev_config, config); + + if (!name || vduse_name_is_valid(name) || + !has_feature(features, VIRTIO_F_VERSION_1) || !config || + !config_size || !ops || !ops->enable_queue || !ops->disable_queue) { + fprintf(stderr, "Invalid parameter for vduse\n"); + return NULL; + } + + dev = calloc(sizeof(VduseDev), 1); + if (!dev) { + fprintf(stderr, "Failed to allocate vduse device\n"); + return NULL; + } + + ctrl_fd = open("/dev/vduse/control", O_RDWR); + if (ctrl_fd < 0) { + fprintf(stderr, "Failed to open /dev/vduse/control: %s\n", + strerror(errno)); + goto err_ctrl; + } + + version = VDUSE_API_VERSION; + if (ioctl(ctrl_fd, VDUSE_SET_API_VERSION, &version)) { + fprintf(stderr, "Failed to set api version %" PRIu64 ": %s\n", + version, strerror(errno)); + goto err_dev; + } + + dev_config = calloc(size + config_size, 1); + if (!dev_config) { + fprintf(stderr, "Failed to allocate config space\n"); + goto err_dev; + } + + strcpy(dev_config->name, name); + dev_config->device_id = device_id; + dev_config->vendor_id = vendor_id; + dev_config->features = features; + dev_config->vq_num = num_queues; + dev_config->vq_align = VDUSE_VQ_ALIGN; + dev_config->config_size = config_size; + memcpy(dev_config->config, config, config_size); + + ret = ioctl(ctrl_fd, VDUSE_CREATE_DEV, dev_config); + free(dev_config); + if (ret && errno != EEXIST) { + fprintf(stderr, "Failed to create vduse device %s: %s\n", + name, strerror(errno)); + goto err_dev; + } + dev->ctrl_fd = ctrl_fd; + + ret = vduse_dev_init(dev, name, num_queues, ops, priv); + if (ret < 0) { + fprintf(stderr, "Failed to init vduse device %s: %s\n", + name, strerror(ret)); + goto err; + } + + return dev; +err: + ioctl(ctrl_fd, VDUSE_DESTROY_DEV, name); +err_dev: + close(ctrl_fd); +err_ctrl: + free(dev); + + return NULL; +} + +int vduse_dev_destroy(VduseDev *dev) +{ + size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE); + int i, ret = 0; + + if (dev->log) { + munmap(dev->log, log_size); + } + for (i = 0; i < dev->num_queues; i++) { + free(dev->vqs[i].resubmit_list); + } + free(dev->vqs); + if (dev->fd >= 0) { + close(dev->fd); + dev->fd = -1; + } + if (dev->ctrl_fd >= 0) { + if (ioctl(dev->ctrl_fd, VDUSE_DESTROY_DEV, dev->name)) { + ret = -errno; + } + close(dev->ctrl_fd); + dev->ctrl_fd = -1; + } + free(dev->name); + free(dev); + + return ret; +} diff --git a/subprojects/libvduse/libvduse.h b/subprojects/libvduse/libvduse.h new file mode 100644 index 00000000000..32f19e7b486 --- /dev/null +++ b/subprojects/libvduse/libvduse.h @@ -0,0 +1,247 @@ +/* + * VDUSE (vDPA Device in Userspace) library + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef LIBVDUSE_H +#define LIBVDUSE_H + +#include +#include + +#define VIRTQUEUE_MAX_SIZE 1024 + +/* VDUSE device structure */ +typedef struct VduseDev VduseDev; + +/* Virtqueue structure */ +typedef struct VduseVirtq VduseVirtq; + +/* Some operation of VDUSE backend */ +typedef struct VduseOps { + /* Called when virtqueue can be processed */ + void (*enable_queue)(VduseDev *dev, VduseVirtq *vq); + /* Called when virtqueue processing should be stopped */ + void (*disable_queue)(VduseDev *dev, VduseVirtq *vq); +} VduseOps; + +/* Describing elements of the I/O buffer */ +typedef struct VduseVirtqElement { + /* Descriptor table index */ + unsigned int index; + /* Number of physically-contiguous device-readable descriptors */ + unsigned int out_num; + /* Number of physically-contiguous device-writable descriptors */ + unsigned int in_num; + /* Array to store physically-contiguous device-writable descriptors */ + struct iovec *in_sg; + /* Array to store physically-contiguous device-readable descriptors */ + struct iovec *out_sg; +} VduseVirtqElement; + + +/** + * vduse_get_virtio_features: + * + * Get supported virtio features + * + * Returns: supported feature bits + */ +uint64_t vduse_get_virtio_features(void); + +/** + * vduse_queue_get_dev: + * @vq: specified virtqueue + * + * Get corresponding VDUSE device from the virtqueue. + * + * Returns: a pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_queue_get_dev(VduseVirtq *vq); + +/** + * vduse_queue_get_fd: + * @vq: specified virtqueue + * + * Get the kick fd for the virtqueue. + * + * Returns: file descriptor on success, -1 on failure. + */ +int vduse_queue_get_fd(VduseVirtq *vq); + +/** + * vduse_queue_pop: + * @vq: specified virtqueue + * @sz: the size of struct to return (must be >= VduseVirtqElement) + * + * Pop an element from virtqueue available ring. + * + * Returns: a pointer to a structure containing VduseVirtqElement on success, + * NULL on failure. + */ +void *vduse_queue_pop(VduseVirtq *vq, size_t sz); + +/** + * vduse_queue_push: + * @vq: specified virtqueue + * @elem: pointer to VduseVirtqElement returned by vduse_queue_pop() + * @len: length in bytes to write + * + * Push an element to virtqueue used ring. + */ +void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem, + unsigned int len); +/** + * vduse_queue_notify: + * @vq: specified virtqueue + * + * Request to notify the queue. + */ +void vduse_queue_notify(VduseVirtq *vq); + +/** + * vduse_dev_get_priv: + * @dev: VDUSE device + * + * Get the private pointer passed to vduse_dev_create(). + * + * Returns: private pointer on success, NULL on failure. + */ +void *vduse_dev_get_priv(VduseDev *dev); + +/** + * vduse_dev_get_queue: + * @dev: VDUSE device + * @index: virtqueue index + * + * Get the specified virtqueue. + * + * Returns: a pointer to the virtqueue on success, NULL on failure. + */ +VduseVirtq *vduse_dev_get_queue(VduseDev *dev, int index); + +/** + * vduse_dev_get_fd: + * @dev: VDUSE device + * + * Get the control message fd for the VDUSE device. + * + * Returns: file descriptor on success, -1 on failure. + */ +int vduse_dev_get_fd(VduseDev *dev); + +/** + * vduse_dev_handler: + * @dev: VDUSE device + * + * Used to process the control message. + * + * Returns: file descriptor on success, -errno on failure. + */ +int vduse_dev_handler(VduseDev *dev); + +/** + * vduse_dev_update_config: + * @dev: VDUSE device + * @size: the size to write to configuration space + * @offset: the offset from the beginning of configuration space + * @buffer: the buffer used to write from + * + * Update device configuration space and inject a config interrupt. + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_dev_update_config(VduseDev *dev, uint32_t size, + uint32_t offset, char *buffer); + +/** + * vduse_dev_setup_queue: + * @dev: VDUSE device + * @index: virtqueue index + * @max_size: the max size of virtqueue + * + * Setup the specified virtqueue. + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size); + +/** + * vduse_set_reconnect_log_file: + * @dev: VDUSE device + * @file: filename of reconnect log + * + * Specify the file to store log for reconnecting. It should + * be called before vduse_dev_setup_queue(). + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_set_reconnect_log_file(VduseDev *dev, const char *filename); + +/** + * vduse_dev_create_by_fd: + * @fd: passed file descriptor + * @num_queues: the number of virtqueues + * @ops: the operation of VDUSE backend + * @priv: private pointer + * + * Create VDUSE device from a passed file descriptor. + * + * Returns: pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues, + const VduseOps *ops, void *priv); + +/** + * vduse_dev_create_by_name: + * @name: VDUSE device name + * @num_queues: the number of virtqueues + * @ops: the operation of VDUSE backend + * @priv: private pointer + * + * Create VDUSE device on /dev/vduse/$NAME. + * + * Returns: pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_dev_create_by_name(const char *name, uint16_t num_queues, + const VduseOps *ops, void *priv); + +/** + * vduse_dev_create: + * @name: VDUSE device name + * @device_id: virtio device id + * @vendor_id: virtio vendor id + * @features: virtio features + * @num_queues: the number of virtqueues + * @config_size: the size of the configuration space + * @config: the buffer of the configuration space + * @ops: the operation of VDUSE backend + * @priv: private pointer + * + * Create VDUSE device. + * + * Returns: pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_dev_create(const char *name, uint32_t device_id, + uint32_t vendor_id, uint64_t features, + uint16_t num_queues, uint32_t config_size, + char *config, const VduseOps *ops, void *priv); + +/** + * vduse_dev_destroy: + * @dev: VDUSE device + * + * Destroy the VDUSE device. + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_dev_destroy(VduseDev *dev); + +#endif diff --git a/subprojects/libvduse/linux-headers/linux b/subprojects/libvduse/linux-headers/linux new file mode 120000 index 00000000000..04f3304f79c --- /dev/null +++ b/subprojects/libvduse/linux-headers/linux @@ -0,0 +1 @@ +../../../linux-headers/linux/ \ No newline at end of file diff --git a/subprojects/libvduse/meson.build b/subprojects/libvduse/meson.build new file mode 100644 index 00000000000..72b9c6f8310 --- /dev/null +++ b/subprojects/libvduse/meson.build @@ -0,0 +1,14 @@ +project('libvduse', 'c', + license: 'GPL-2.0-or-later', + default_options: ['c_std=gnu99']) + +keyval = import('keyval') +config_host = keyval.load(meson.global_build_root() / 'config-host.mak') + +libvduse = static_library('vduse', + files('libvduse.c'), + c_args: '-D_GNU_SOURCE', + pic: 'AS_SHARED_LIB' in config_host) + +libvduse_dep = declare_dependency(link_with: libvduse, + include_directories: include_directories('.')) diff --git a/subprojects/libvduse/standard-headers/linux b/subprojects/libvduse/standard-headers/linux new file mode 120000 index 00000000000..c416f068ac7 --- /dev/null +++ b/subprojects/libvduse/standard-headers/linux @@ -0,0 +1 @@ +../../../include/standard-headers/linux/ \ No newline at end of file diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index b4cc3c2d680..ffed4729a3d 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -779,15 +779,9 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { /* Send the message back to qemu with the addresses filled in. */ vmsg->fd_num = 0; - if (!vu_send_reply(dev, dev->sock, vmsg)) { - vu_panic(dev, "failed to respond to add-mem-region for postcopy"); - return false; - } - DPRINT("Successfully added new region in postcopy\n"); dev->nregions++; - return false; - + return true; } else { for (i = 0; i < dev->max_queues; i++) { if (dev->vq[i].vring.desc) { @@ -1827,18 +1821,11 @@ vu_handle_vring_kick(VuDev *dev, VhostUserMsg *vmsg) static bool vu_handle_get_max_memslots(VuDev *dev, VhostUserMsg *vmsg) { - vmsg->flags = VHOST_USER_REPLY_MASK | VHOST_USER_VERSION; - vmsg->size = sizeof(vmsg->payload.u64); - vmsg->payload.u64 = VHOST_USER_MAX_RAM_SLOTS; - vmsg->fd_num = 0; - - if (!vu_message_write(dev, dev->sock, vmsg)) { - vu_panic(dev, "Failed to send max ram slots: %s\n", strerror(errno)); - } + vmsg_set_reply_u64(vmsg, VHOST_USER_MAX_RAM_SLOTS); DPRINT("u64: 0x%016"PRIx64"\n", (uint64_t) VHOST_USER_MAX_RAM_SLOTS); - return false; + return true; } static bool diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.h new file mode 100644 index 00000000000..629d75ca5a7 --- /dev/null +++ b/target/arm/common-semi-target.h @@ -0,0 +1,62 @@ +/* + * Target-specific parts of semihosting/arm-compat-semi.c. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019, 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_ARM_COMMON_SEMI_TARGET_H +#define TARGET_ARM_COMMON_SEMI_TARGET_H + +#ifndef CONFIG_USER_ONLY +#include "hw/arm/boot.h" +#endif + +static inline target_ulong common_semi_arg(CPUState *cs, int argno) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (is_a64(env)) { + return env->xregs[argno]; + } else { + return env->regs[argno]; + } +} + +static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (is_a64(env)) { + env->xregs[0] = ret; + } else { + env->regs[0] = ret; + } +} + +static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +{ + return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); +} + +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return is_a64(env); +} + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + return is_a64(env) ? env->xregs[31] : env->regs[13]; +} + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + /* Ok for A64, invalid for A32/T32 */ + return is_a64(env); +} + +#endif diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index d9b678c2f17..7e78c2c05c6 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -113,6 +113,11 @@ enum { ARM_CP_EL3_NO_EL2_UNDEF = 1 << 16, ARM_CP_EL3_NO_EL2_KEEP = 1 << 17, ARM_CP_EL3_NO_EL2_C_NZ = 1 << 18, + /* + * Flag: Access check for this sysreg is constrained by the + * ARM pseudocode function CheckSMEAccess(). + */ + ARM_CP_SME = 1 << 19, }; /* @@ -437,6 +442,9 @@ void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, /* CPReadFn that can be used for read-as-zero behaviour */ uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri); +/* CPWriteFn that just writes the value to ri->fieldoffset */ +void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); + /* * CPResetFn that does nothing, for use if no reset is required even * if fieldoffset is non zero. diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1b5d5357880..1b7b3d76bb3 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -39,6 +39,7 @@ #include "hw/boards.h" #endif #include "sysemu/tcg.h" +#include "sysemu/qtest.h" #include "sysemu/hw_accel.h" #include "kvm_arm.h" #include "disas/capstone.h" @@ -203,19 +204,29 @@ static void arm_cpu_reset(DeviceState *dev) /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, CPACR_EL1, FPEN, 3); - /* and to the SVE instructions */ - env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, - CPACR_EL1, ZEN, 3); - /* with reasonable vector length */ + /* and to the SVE instructions, with default vector length */ if (cpu_isar_feature(aa64_sve, cpu)) { + env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, + CPACR_EL1, ZEN, 3); env->vfp.zcr_el[1] = cpu->sve_default_vq - 1; } + /* and for SME instructions, with default vector length, and TPIDR2 */ + if (cpu_isar_feature(aa64_sme, cpu)) { + env->cp15.sctlr_el[1] |= SCTLR_EnTP2; + env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, + CPACR_EL1, SMEN, 3); + env->vfp.smcr_el[1] = cpu->sme_default_vq - 1; + if (cpu_isar_feature(aa64_sme_fa64, cpu)) { + env->vfp.smcr_el[1] = FIELD_DP64(env->vfp.smcr_el[1], + SMCR, FA64, 1); + } + } /* * Enable 48-bit address space (TODO: take reserved_va into account). * Enable TBI0 but not TBI1. * Note that this must match useronly_clean_ptr. */ - env->cp15.tcr_el[1].raw_tcr = 5 | (1ULL << 37); + env->cp15.tcr_el[1] = 5 | (1ULL << 37); /* Enable MTE */ if (cpu_isar_feature(aa64_mte, cpu)) { @@ -827,13 +838,6 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) bool sctlr_b; if (is_a64(env)) { - /* We might not be compiled with the A64 disassembler - * because it needs a C++ compiler. Leave print_insn - * unset in this case to use the caller default behaviour. - */ -#if defined(CONFIG_ARM_A64_DIS) - info->print_insn = print_insn_arm_a64; -#endif info->cap_arch = CS_ARCH_ARM64; info->cap_insn_unit = 4; info->cap_insn_split = 4; @@ -884,6 +888,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) int i; int el = arm_current_el(env); const char *ns_status; + bool sve; qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); for (i = 0; i < 32; i++) { @@ -910,6 +915,12 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) el, psr & PSTATE_SP ? 'h' : 't'); + if (cpu_isar_feature(aa64_sme, cpu)) { + qemu_fprintf(f, " SVCR=%08" PRIx64 " %c%c", + env->svcr, + (FIELD_EX64(env->svcr, SVCR, ZA) ? 'Z' : '-'), + (FIELD_EX64(env->svcr, SVCR, SM) ? 'S' : '-')); + } if (cpu_isar_feature(aa64_bti, cpu)) { qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10); } @@ -924,7 +935,15 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " FPCR=%08x FPSR=%08x\n", vfp_get_fpcr(env), vfp_get_fpsr(env)); - if (cpu_isar_feature(aa64_sve, cpu) && sve_exception_el(env, el) == 0) { + if (cpu_isar_feature(aa64_sme, cpu) && FIELD_EX64(env->svcr, SVCR, SM)) { + sve = sme_exception_el(env, el) == 0; + } else if (cpu_isar_feature(aa64_sve, cpu)) { + sve = sve_exception_el(env, el) == 0; + } else { + sve = false; + } + + if (sve) { int j, zcr_len = sve_vqm1_for_el(env, el); for (i = 0; i <= FFR_PRED_NUM; i++) { @@ -1122,11 +1141,13 @@ static void arm_cpu_initfn(Object *obj) #ifdef CONFIG_USER_ONLY # ifdef TARGET_AARCH64 /* - * The linux kernel defaults to 512-bit vectors, when sve is supported. - * See documentation for /proc/sys/abi/sve_default_vector_length, and - * our corresponding sve-default-vector-length cpu property. + * The linux kernel defaults to 512-bit for SVE, and 256-bit for SME. + * These values were chosen to fit within the default signal frame. + * See documentation for /proc/sys/abi/{sve,sme}_default_vector_length, + * and our corresponding cpu property. */ cpu->sve_default_vq = 4; + cpu->sme_default_vq = 2; # endif #else /* Our inbound IRQ and FIQ lines */ @@ -1421,6 +1442,7 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) { Error *local_err = NULL; +#ifdef TARGET_AARCH64 if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { arm_cpu_sve_finalize(cpu, &local_err); if (local_err != NULL) { @@ -1428,6 +1450,12 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) return; } + arm_cpu_sme_finalize(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + arm_cpu_pauth_finalize(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1440,6 +1468,7 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) return; } } +#endif if (kvm_enabled()) { kvm_arm_steal_time_finalize(cpu, &local_err); @@ -1490,25 +1519,32 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } - if (kvm_enabled()) { + if (!tcg_enabled() && !qtest_enabled()) { /* + * We assume that no accelerator except TCG (and the "not really an + * accelerator" qtest) can handle these features, because Arm hardware + * virtualization can't virtualize them. + * * Catch all the cases which might cause us to create more than one * address space for the CPU (otherwise we will assert() later in * cpu_address_space_init()). */ if (arm_feature(env, ARM_FEATURE_M)) { error_setg(errp, - "Cannot enable KVM when using an M-profile guest CPU"); + "Cannot enable %s when using an M-profile guest CPU", + current_accel_name()); return; } if (cpu->has_el3) { error_setg(errp, - "Cannot enable KVM when guest CPU has EL3 enabled"); + "Cannot enable %s when guest CPU has EL3 enabled", + current_accel_name()); return; } if (cpu->tag_memory) { error_setg(errp, - "Cannot enable KVM when guest CPUs has MTE enabled"); + "Cannot enable %s when guest CPUs has MTE enabled", + current_accel_name()); return; } } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index df677b2d5d2..e890ee074d3 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -166,12 +166,6 @@ typedef struct ARMGenericTimer { #define GTIMER_HYPVIRT 4 #define NUM_GTIMERS 5 -typedef struct { - uint64_t raw_tcr; - uint32_t mask; - uint32_t base_mask; -} TCR; - #define VTCR_NSW (1u << 29) #define VTCR_NSA (1u << 30) #define VSTCR_SW VTCR_NSW @@ -205,14 +199,8 @@ typedef struct { #ifdef TARGET_AARCH64 # define ARM_MAX_VQ 16 -void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp); -void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp); -void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp); #else # define ARM_MAX_VQ 1 -static inline void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { } -static inline void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) { } -static inline void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) { } #endif typedef struct ARMVectorReg { @@ -258,6 +246,7 @@ typedef struct CPUArchState { * nRW (also known as M[4]) is kept, inverted, in env->aarch64 * DAIF (exception masks) are kept in env->daif * BTYPE is kept in env->btype + * SM and ZA are kept in env->svcr * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; @@ -292,6 +281,7 @@ typedef struct CPUArchState { uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ uint32_t btype; /* BTI branch type. spsr[11:10]. */ uint64_t daif; /* exception masks, in the bits they are in PSTATE */ + uint64_t svcr; /* PSTATE.{SM,ZA} in the bits they are in SVCR */ uint64_t elr_el[4]; /* AArch64 exception link regs */ uint64_t sp_el[4]; /* AArch64 banked stack pointers */ @@ -343,9 +333,9 @@ typedef struct CPUArchState { uint64_t vttbr_el2; /* Virtualization Translation Table Base. */ uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */ /* MMU translation table base control. */ - TCR tcr_el[4]; - TCR vtcr_el2; /* Virtualization Translation Control. */ - TCR vstcr_el2; /* Secure Virtualization Translation Control. */ + uint64_t tcr_el[4]; + uint64_t vtcr_el2; /* Virtualization Translation Control. */ + uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */ uint32_t c2_data; /* MPU data cacheable bits. */ uint32_t c2_insn; /* MPU instruction cacheable bits. */ union { /* MMU domain access control register @@ -474,6 +464,7 @@ typedef struct CPUArchState { }; uint64_t tpidr_el[4]; }; + uint64_t tpidr2_el0; /* The secure banks of these registers don't map anywhere */ uint64_t tpidrurw_s; uint64_t tpidrprw_s; @@ -503,6 +494,7 @@ typedef struct CPUArchState { uint64_t dbgwcr[16]; /* watchpoint control registers */ uint64_t mdscr_el1; uint64_t oslsr_el1; /* OS Lock Status */ + uint64_t osdlr_el1; /* OS DoubleLock status */ uint64_t mdcr_el2; uint64_t mdcr_el3; /* Stores the architectural value of the counter *the last time it was @@ -666,8 +658,8 @@ typedef struct CPUArchState { float_status standard_fp_status; float_status standard_fp_status_f16; - /* ZCR_EL[1-3] */ - uint64_t zcr_el[4]; + uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ + uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ } vfp; uint64_t exclusive_addr; uint64_t exclusive_val; @@ -691,6 +683,28 @@ typedef struct CPUArchState { } keys; uint64_t scxtnum_el[4]; + + /* + * SME ZA storage -- 256 x 256 byte array, with bytes in host word order, + * as we do with vfp.zregs[]. This corresponds to the architectural ZA + * array, where ZA[N] is in the least-significant bytes of env->zarray[N]. + * When SVL is less than the architectural maximum, the accessible + * storage is restricted, such that if the SVL is X bytes the guest can + * see only the bottom X elements of zarray[], and only the least + * significant X bytes of each element of the array. (In other words, + * the observable part is always square.) + * + * The ZA storage can also be considered as a set of square tiles of + * elements of different sizes. The mapping from tiles to the ZA array + * is architecturally defined, such that for tiles of elements of esz + * bytes, the Nth row (or "horizontal slice") of tile T is in + * ZA[T + N * esz]. Note that this means that each tile is not contiguous + * in the ZA storage, because its rows are striped through the ZA array. + * + * Because this is so large, keep this toward the end of the reset area, + * to keep the offsets into the rest of the structure smaller. + */ + ARMVectorReg zarray[ARM_MAX_VQ * 16]; #endif #if defined(CONFIG_USER_ONLY) @@ -782,6 +796,19 @@ typedef enum ARMPSCIState { typedef struct ARMISARegisters ARMISARegisters; +/* + * In map, each set bit is a supported vector length of (bit-number + 1) * 16 + * bytes, i.e. each bit number + 1 is the vector length in quadwords. + * + * While processing properties during initialization, corresponding init bits + * are set for bits in sve_vq_map that have been set by properties. + * + * Bits set in supported represent valid vector lengths for the CPU type. + */ +typedef struct { + uint32_t map, init, supported; +} ARMVQMap; + /** * ARMCPU: * @env: #CPUARMState @@ -956,6 +983,8 @@ struct ArchCPU { uint32_t mvfr2; uint32_t id_dfr0; uint32_t dbgdidr; + uint32_t dbgdevid; + uint32_t dbgdevid1; uint64_t id_aa64isar0; uint64_t id_aa64isar1; uint64_t id_aa64pfr0; @@ -1028,23 +1057,11 @@ struct ArchCPU { #ifdef CONFIG_USER_ONLY /* Used to set the default vector length at process start. */ uint32_t sve_default_vq; + uint32_t sme_default_vq; #endif - /* - * In sve_vq_map each set bit is a supported vector length of - * (bit-number + 1) * 16 bytes, i.e. each bit number + 1 is the vector - * length in quadwords. - * - * While processing properties during initialization, corresponding - * sve_vq_init bits are set for bits in sve_vq_map that have been - * set by properties. - * - * Bits set in sve_vq_supported represent valid vector lengths for - * the CPU type. - */ - uint32_t sve_vq_map; - uint32_t sve_vq_init; - uint32_t sve_vq_supported; + ARMVQMap sve_vq; + ARMVQMap sme_vq; /* Generic timer counter frequency, in Hz */ uint64_t gt_cntfrq_hz; @@ -1093,8 +1110,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); void aarch64_sve_change_el(CPUARMState *env, int old_el, int new_el, bool el0_a64); -void aarch64_add_sve_properties(Object *obj); -void aarch64_add_pauth_properties(Object *obj); +void arm_reset_sve_state(CPUARMState *env); /* * SVE registers are encoded in KVM's memory in an endianness-invariant format. @@ -1125,7 +1141,6 @@ static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } static inline void aarch64_sve_change_el(CPUARMState *env, int o, int n, bool a) { } -static inline void aarch64_add_sve_properties(Object *obj) { } #endif void aarch64_sync_32_to_64(CPUARMState *env); @@ -1133,15 +1148,21 @@ void aarch64_sync_64_to_32(CPUARMState *env); int fp_exception_el(CPUARMState *env, int cur_el); int sve_exception_el(CPUARMState *env, int cur_el); +int sme_exception_el(CPUARMState *env, int cur_el); /** - * sve_vqm1_for_el: + * sve_vqm1_for_el_sm: * @env: CPUARMState * @el: exception level + * @sm: streaming mode * - * Compute the current SVE vector length for @el, in units of + * Compute the current vector length for @el & @sm, in units of * Quadwords Minus 1 -- the same scale used for ZCR_ELx.LEN. + * If @sm, compute for SVL, otherwise NVL. */ +uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm); + +/* Likewise, but using @sm = PSTATE.SM. */ uint32_t sve_vqm1_for_el(CPUARMState *env, int el); static inline bool is_a64(CPUARMState *env) @@ -1391,6 +1412,25 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #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. @@ -1426,6 +1466,14 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define PSTATE_MODE_EL1t 4 #define PSTATE_MODE_EL0t 0 +/* PSTATE bits that are accessed via SVCR and not stored in SPSR_ELx. */ +FIELD(SVCR, SM, 0, 1) +FIELD(SVCR, ZA, 1, 1) + +/* Fields for SMCR_ELx. */ +FIELD(SMCR, LEN, 0, 4) +FIELD(SMCR, FA64, 31, 1) + /* Write a new value to v7m.exception, thus transitioning into or out * of Handler mode; this may result in a change of active stack pointer. */ @@ -2219,6 +2267,15 @@ FIELD(DBGDIDR, CTX_CMPS, 20, 4) FIELD(DBGDIDR, BRPS, 24, 4) FIELD(DBGDIDR, WRPS, 28, 4) +FIELD(DBGDEVID, PCSAMPLE, 0, 4) +FIELD(DBGDEVID, WPADDRMASK, 4, 4) +FIELD(DBGDEVID, BPADDRMASK, 8, 4) +FIELD(DBGDEVID, VECTORCATCH, 12, 4) +FIELD(DBGDEVID, VIRTEXTNS, 16, 4) +FIELD(DBGDEVID, DOUBLELOCK, 20, 4) +FIELD(DBGDEVID, AUXREGS, 24, 4) +FIELD(DBGDEVID, CIDMASK, 28, 4) + FIELD(MVFR0, SIMDREG, 0, 4) FIELD(MVFR0, FPSP, 4, 4) FIELD(MVFR0, FPDP, 8, 4) @@ -3114,6 +3171,11 @@ FIELD(TBFLAG_A32, HSTR_ACTIVE, 9, 1) * the same thing as the current security state of the processor! */ FIELD(TBFLAG_A32, NS, 10, 1) +/* + * Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. + * This requires an SME trap from AArch32 mode when using NEON. + */ +FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) /* * Bit usage when in AArch32 state, for M-profile only. @@ -3147,6 +3209,12 @@ FIELD(TBFLAG_A64, ATA, 15, 1) FIELD(TBFLAG_A64, TCMA, 16, 2) FIELD(TBFLAG_A64, MTE_ACTIVE, 18, 1) FIELD(TBFLAG_A64, MTE0_ACTIVE, 19, 1) +FIELD(TBFLAG_A64, SMEEXC_EL, 20, 2) +FIELD(TBFLAG_A64, PSTATE_SM, 22, 1) +FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1) +FIELD(TBFLAG_A64, SVL, 24, 4) +/* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ +FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) /* * Helpers for using the above. @@ -3192,6 +3260,17 @@ static inline int sve_vq(CPUARMState *env) return EX_TBFLAG_A64(env->hflags, VL) + 1; } +/** + * sme_vq + * @env: the cpu context + * + * Return the SVL cached within env->hflags, in units of quadwords. + */ +static inline int sme_vq(CPUARMState *env) +{ + return EX_TBFLAG_A64(env->hflags, SVL) + 1; +} + static inline bool bswap_code(bool sctlr_b) { #ifdef CONFIG_USER_ONLY @@ -3326,9 +3405,12 @@ static inline MemTxAttrs *typecheck_memtxattrs(MemTxAttrs *x) /* * AArch64 usage of the PAGE_TARGET_* bits for linux-user. + * Note that with the Linux kernel, PROT_MTE may not be cleared by mprotect + * mprotect but PROT_BTI may be cleared. C.f. the kernel's VM_ARCH_CLEAR. */ -#define PAGE_BTI PAGE_TARGET_1 -#define PAGE_MTE PAGE_TARGET_2 +#define PAGE_BTI PAGE_TARGET_1 +#define PAGE_MTE PAGE_TARGET_2 +#define PAGE_TARGET_STICKY PAGE_MTE #ifdef TARGET_TAGGED_ADDRESSES /** @@ -3672,11 +3754,21 @@ 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. */ @@ -4101,6 +4193,11 @@ 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?" */ diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 15665c962b2..78e27f778ac 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -79,6 +79,8 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.id_aa64isar0 = 0x00011120; cpu->isar.id_aa64mmfr0 = 0x00001124; cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41013000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ @@ -134,6 +136,8 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.id_aa64isar0 = 0x00011120; cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x00110f13; + cpu->isar.dbgdevid1 = 0x1; cpu->isar.reset_pmcr_el0 = 0x41033000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ @@ -187,6 +191,8 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.id_aa64isar0 = 0x00011120; cpu->isar.id_aa64mmfr0 = 0x00001124; cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41023000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ @@ -355,8 +361,8 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * any of the above. Finally, if SVE is not disabled, then at least one * vector length must be enabled. */ - uint32_t vq_map = cpu->sve_vq_map; - uint32_t vq_init = cpu->sve_vq_init; + uint32_t vq_map = cpu->sve_vq.map; + uint32_t vq_init = cpu->sve_vq.init; uint32_t vq_supported; uint32_t vq_mask = 0; uint32_t tmp, vq, max_vq = 0; @@ -369,14 +375,14 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) */ if (kvm_enabled()) { if (kvm_arm_sve_supported()) { - cpu->sve_vq_supported = kvm_arm_sve_get_vls(CPU(cpu)); - vq_supported = cpu->sve_vq_supported; + cpu->sve_vq.supported = kvm_arm_sve_get_vls(CPU(cpu)); + vq_supported = cpu->sve_vq.supported; } else { assert(!cpu_isar_feature(aa64_sve, cpu)); vq_supported = 0; } } else { - vq_supported = cpu->sve_vq_supported; + vq_supported = cpu->sve_vq.supported; } /* @@ -487,8 +493,13 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) "using only sve properties.\n"); } else { error_setg(errp, "cannot enable sve%d", vq * 128); - error_append_hint(errp, "This CPU does not support " - "the vector length %d-bits.\n", vq * 128); + if (vq_supported) { + error_append_hint(errp, "This CPU does not support " + "the vector length %d-bits.\n", vq * 128); + } else { + error_append_hint(errp, "SVE not supported by KVM " + "on this host\n"); + } } return; } else { @@ -529,7 +540,7 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) /* From now on sve_max_vq is the actual maximum supported length. */ cpu->sve_max_vq = max_vq; - cpu->sve_vq_map = vq_map; + cpu->sve_vq.map = vq_map; } static void cpu_max_get_sve_max_vq(Object *obj, Visitor *v, const char *name, @@ -574,31 +585,34 @@ static void cpu_max_set_sve_max_vq(Object *obj, Visitor *v, const char *name, } /* - * Note that cpu_arm_get/set_sve_vq cannot use the simpler - * object_property_add_bool interface because they make use - * of the contents of "name" to determine which bit on which - * to operate. + * Note that cpu_arm_{get,set}_vq cannot use the simpler + * object_property_add_bool interface because they make use of the + * contents of "name" to determine which bit on which to operate. */ -static void cpu_arm_get_sve_vq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void cpu_arm_get_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); + ARMVQMap *vq_map = opaque; uint32_t vq = atoi(&name[3]) / 128; + bool sve = vq_map == &cpu->sve_vq; bool value; - /* All vector lengths are disabled when SVE is off. */ - if (!cpu_isar_feature(aa64_sve, cpu)) { + /* All vector lengths are disabled when feature is off. */ + if (sve + ? !cpu_isar_feature(aa64_sve, cpu) + : !cpu_isar_feature(aa64_sme, cpu)) { value = false; } else { - value = extract32(cpu->sve_vq_map, vq - 1, 1); + value = extract32(vq_map->map, vq - 1, 1); } visit_type_bool(v, name, &value, errp); } -static void cpu_arm_set_sve_vq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void cpu_arm_set_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - ARMCPU *cpu = ARM_CPU(obj); + ARMVQMap *vq_map = opaque; uint32_t vq = atoi(&name[3]) / 128; bool value; @@ -606,14 +620,8 @@ static void cpu_arm_set_sve_vq(Object *obj, Visitor *v, const char *name, return; } - if (value && kvm_enabled() && !kvm_arm_sve_supported()) { - error_setg(errp, "cannot enable %s", name); - error_append_hint(errp, "SVE not supported by KVM on this host\n"); - return; - } - - cpu->sve_vq_map = deposit32(cpu->sve_vq_map, vq - 1, 1, value); - cpu->sve_vq_init |= 1 << (vq - 1); + vq_map->map = deposit32(vq_map->map, vq - 1, 1, value); + vq_map->init |= 1 << (vq - 1); } static bool cpu_arm_get_sve(Object *obj, Error **errp) @@ -637,13 +645,85 @@ static void cpu_arm_set_sve(Object *obj, bool value, Error **errp) cpu->isar.id_aa64pfr0 = t; } -#ifdef CONFIG_USER_ONLY -/* Mirror linux /proc/sys/abi/sve_default_vector_length. */ -static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) +{ + uint32_t vq_map = cpu->sme_vq.map; + uint32_t vq_init = cpu->sme_vq.init; + uint32_t vq_supported = cpu->sme_vq.supported; + uint32_t vq; + + if (vq_map == 0) { + if (!cpu_isar_feature(aa64_sme, cpu)) { + cpu->isar.id_aa64smfr0 = 0; + return; + } + + /* TODO: KVM will require limitations via SMCR_EL2. */ + vq_map = vq_supported & ~vq_init; + + if (vq_map == 0) { + vq = ctz32(vq_supported) + 1; + error_setg(errp, "cannot disable sme%d", vq * 128); + error_append_hint(errp, "All SME vector lengths are disabled.\n"); + error_append_hint(errp, "With SME enabled, at least one " + "vector length must be enabled.\n"); + return; + } + } else { + if (!cpu_isar_feature(aa64_sme, cpu)) { + vq = 32 - clz32(vq_map); + error_setg(errp, "cannot enable sme%d", vq * 128); + error_append_hint(errp, "SME must be enabled to enable " + "vector lengths.\n"); + error_append_hint(errp, "Add sme=on to the CPU property list.\n"); + return; + } + /* TODO: KVM will require limitations via SMCR_EL2. */ + } + + cpu->sme_vq.map = vq_map; +} + +static bool cpu_arm_get_sme(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + return cpu_isar_feature(aa64_sme, cpu); +} + +static void cpu_arm_set_sme(Object *obj, bool value, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); + uint64_t t; + + t = cpu->isar.id_aa64pfr1; + t = FIELD_DP64(t, ID_AA64PFR1, SME, value); + cpu->isar.id_aa64pfr1 = t; +} + +static bool cpu_arm_get_sme_fa64(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + return cpu_isar_feature(aa64_sme, cpu) && + cpu_isar_feature(aa64_sme_fa64, cpu); +} + +static void cpu_arm_set_sme_fa64(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint64_t t; + + t = cpu->isar.id_aa64smfr0; + t = FIELD_DP64(t, ID_AA64SMFR0, FA64, value); + cpu->isar.id_aa64smfr0 = t; +} + +#ifdef CONFIG_USER_ONLY +/* Mirror linux /proc/sys/abi/{sve,sme}_default_vector_length. */ +static void cpu_arm_set_default_vec_len(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint32_t *ptr_default_vq = opaque; int32_t default_len, default_vq, remainder; if (!visit_type_int32(v, name, &default_len, errp)) { @@ -652,7 +732,7 @@ static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, /* Undocumented, but the kernel allows -1 to indicate "maximum". */ if (default_len == -1) { - cpu->sve_default_vq = ARM_MAX_VQ; + *ptr_default_vq = ARM_MAX_VQ; return; } @@ -664,7 +744,11 @@ static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, * and is the maximum architectural width of ZCR_ELx.LEN. */ if (remainder || default_vq < 1 || default_vq > 512) { - error_setg(errp, "cannot set sve-default-vector-length"); + ARMCPU *cpu = ARM_CPU(obj); + const char *which = + (ptr_default_vq == &cpu->sve_default_vq ? "sve" : "sme"); + + error_setg(errp, "cannot set %s-default-vector-length", which); if (remainder) { error_append_hint(errp, "Vector length not a multiple of 16\n"); } else if (default_vq < 1) { @@ -676,22 +760,23 @@ static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, return; } - cpu->sve_default_vq = default_vq; + *ptr_default_vq = default_vq; } -static void cpu_arm_get_sve_default_vec_len(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +static void cpu_arm_get_default_vec_len(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { - ARMCPU *cpu = ARM_CPU(obj); - int32_t value = cpu->sve_default_vq * 16; + uint32_t *ptr_default_vq = opaque; + int32_t value = *ptr_default_vq * 16; visit_type_int32(v, name, &value, errp); } #endif -void aarch64_add_sve_properties(Object *obj) +static void aarch64_add_sve_properties(Object *obj) { + ARMCPU *cpu = ARM_CPU(obj); uint32_t vq; object_property_add_bool(obj, "sve", cpu_arm_get_sve, cpu_arm_set_sve); @@ -699,15 +784,41 @@ void aarch64_add_sve_properties(Object *obj) for (vq = 1; vq <= ARM_MAX_VQ; ++vq) { char name[8]; sprintf(name, "sve%d", vq * 128); - object_property_add(obj, name, "bool", cpu_arm_get_sve_vq, - cpu_arm_set_sve_vq, NULL, NULL); + object_property_add(obj, name, "bool", cpu_arm_get_vq, + cpu_arm_set_vq, NULL, &cpu->sve_vq); } #ifdef CONFIG_USER_ONLY /* Mirror linux /proc/sys/abi/sve_default_vector_length. */ object_property_add(obj, "sve-default-vector-length", "int32", - cpu_arm_get_sve_default_vec_len, - cpu_arm_set_sve_default_vec_len, NULL, NULL); + cpu_arm_get_default_vec_len, + cpu_arm_set_default_vec_len, NULL, + &cpu->sve_default_vq); +#endif +} + +static void aarch64_add_sme_properties(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint32_t vq; + + object_property_add_bool(obj, "sme", cpu_arm_get_sme, cpu_arm_set_sme); + object_property_add_bool(obj, "sme_fa64", cpu_arm_get_sme_fa64, + cpu_arm_set_sme_fa64); + + for (vq = 1; vq <= ARM_MAX_VQ; vq <<= 1) { + char name[8]; + sprintf(name, "sme%d", vq * 128); + object_property_add(obj, name, "bool", cpu_arm_get_vq, + cpu_arm_set_vq, NULL, &cpu->sme_vq); + } + +#ifdef CONFIG_USER_ONLY + /* Mirror linux /proc/sys/abi/sme_default_vector_length. */ + object_property_add(obj, "sme-default-vector-length", "int32", + cpu_arm_get_default_vec_len, + cpu_arm_set_default_vec_len, NULL, + &cpu->sme_default_vq); #endif } @@ -751,7 +862,7 @@ static Property arm_cpu_pauth_property = static Property arm_cpu_pauth_impdef_property = DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false); -void aarch64_add_pauth_properties(Object *obj) +static void aarch64_add_pauth_properties(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -913,6 +1024,7 @@ static void aarch64_max_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 */ cpu->isar.id_aa64pfr1 = t; @@ -963,6 +1075,16 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 5); /* FEAT_PMUv3p4 */ cpu->isar.id_aa64dfr0 = t; + t = cpu->isar.id_aa64smfr0; + t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* FEAT_SME_F64F64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* FEAT_SME_I16I64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* FEAT_SME_FA64 */ + cpu->isar.id_aa64smfr0 = t; + /* Replicate the same data to the 32-bit id registers. */ aa32_max_features(cpu); @@ -975,10 +1097,12 @@ static void aarch64_max_initfn(Object *obj) cpu->dcz_blocksize = 7; /* 512 bytes */ #endif - cpu->sve_vq_supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ); + cpu->sve_vq.supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ); + cpu->sme_vq.supported = SVE_VQ_POW2_MAP; aarch64_add_pauth_properties(obj); aarch64_add_sve_properties(obj); + aarch64_add_sme_properties(obj); object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq, cpu_max_set_sve_max_vq, NULL, NULL); qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); @@ -1024,7 +1148,7 @@ static void aarch64_a64fx_initfn(Object *obj) /* The A64FX supports only 128, 256 and 512 bit vector lengths */ aarch64_add_sve_properties(obj); - cpu->sve_vq_supported = (1 << 0) /* 128bit */ + cpu->sve_vq.supported = (1 << 0) /* 128bit */ | (1 << 1) /* 256bit */ | (1 << 3); /* 512bit */ diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c index b751a19c8a7..3099b38e32b 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/cpu_tcg.c @@ -563,6 +563,8 @@ static void cortex_a7_initfn(Object *obj) cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x10011142; cpu->isar.dbgdidr = 0x3515f005; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x1; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -606,6 +608,8 @@ static void cortex_a15_initfn(Object *obj) cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x10011142; cpu->isar.dbgdidr = 0x3515f021; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x0; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -1098,6 +1102,8 @@ static void arm_max_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x00110f13; + cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41013000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index b18a6bd3a23..c21739242c5 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -6,8 +6,10 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "internals.h" +#include "cpregs.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -140,6 +142,9 @@ static bool aa32_generate_debug_exceptions(CPUARMState *env) */ bool arm_generate_debug_exceptions(CPUARMState *env) { + if ((env->cp15.oslsr_el1 & 1) || (env->cp15.osdlr_el1 & 1)) { + return false; + } if (is_a64(env)) { return aa64_generate_debug_exceptions(env); } else { @@ -434,7 +439,7 @@ static uint32_t arm_debug_exception_fsr(CPUARMState *env) using_lpae = true; } else { if (arm_feature(env, ARM_FEATURE_LPAE) && - (env->cp15.tcr_el[target_el].raw_tcr & TTBCR_EAE)) { + (env->cp15.tcr_el[target_el] & TTBCR_EAE)) { using_lpae = true; } } @@ -528,6 +533,581 @@ void HELPER(exception_swstep)(CPUARMState *env, uint32_t syndrome) raise_exception_debug(env, EXCP_UDEF, syndrome); } +/* + * Check for traps to "powerdown debug" registers, which are controlled + * by MDCR.TDOSA + */ +static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tdosa = (mdcr_el2 & MDCR_TDOSA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + + if (el < 2 && mdcr_el2_tdosa) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* + * Check for traps to "debug ROM" registers, which are controlled + * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tdra = (mdcr_el2 & MDCR_TDRA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + + if (el < 2 && mdcr_el2_tdra) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* + * Check for traps to general debug registers, which are controlled + * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + + if (el < 2 && mdcr_el2_tda) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static void oslar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Writes to OSLAR_EL1 may update the OS lock status, which can be + * read via a bit in OSLSR_EL1. + */ + int oslock; + + if (ri->state == ARM_CP_STATE_AA32) { + oslock = (value == 0xC5ACCE55); + } else { + oslock = value & 1; + } + + env->cp15.oslsr_el1 = deposit32(env->cp15.oslsr_el1, 1, 1, oslock); +} + +static void osdlr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + /* + * Only defined bit is bit 0 (DLK); if Feat_DoubleLock is not + * implemented this is RAZ/WI. + */ + if(arm_feature(env, ARM_FEATURE_AARCH64) + ? cpu_isar_feature(aa64_doublelock, cpu) + : cpu_isar_feature(aa32_doublelock, cpu)) { + env->cp15.osdlr_el1 = value & 1; + } +} + +static const ARMCPRegInfo debug_cp_reginfo[] = { + /* + * DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped + * debug components. The AArch64 version of DBGDRAR is named MDRAR_EL1; + * unlike DBGDRAR it is never accessible from EL0. + * DBGDSAR is deprecated and must RAZ from v8 anyway, so it has no AArch64 + * accessor. + */ + { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL1_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ + { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), + .resetvalue = 0 }, + /* + * MDCCSR_EL0[30:29] map to EDSCR[30:29]. Simply RAZ as the external + * Debug Communication Channel is not implemented. + */ + { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, + .access = PL0_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as + * it is unlikely a guest will care. + * We don't implement the configurable EL0 access. + */ + { .name = "DBGDSCRint", .state = ARM_CP_STATE_AA32, + .cp = 14, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, + .type = ARM_CP_ALIAS, + .access = PL1_R, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, + { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, + .access = PL1_W, .type = ARM_CP_NO_RAW, + .accessfn = access_tdosa, + .writefn = oslar_write }, + { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL1_R, .resetvalue = 10, + .accessfn = access_tdosa, + .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, + /* Dummy OSDLR_EL1: 32-bit Linux will read this */ + { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, + .access = PL1_RW, .accessfn = access_tdosa, + .writefn = osdlr_write, + .fieldoffset = offsetof(CPUARMState, cp15.osdlr_el1) }, + /* + * Dummy DBGVCR: Linux wants to clear this on startup, but we don't + * implement vector catch debug events yet. + */ + { .name = "DBGVCR", + .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, + .access = PL1_RW, .accessfn = access_tda, + .type = ARM_CP_NOP }, + /* + * Dummy DBGVCR32_EL2 (which is only for a 64-bit hypervisor + * to save and restore a 32-bit guest's DBGVCR) + */ + { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, + .access = PL2_RW, .accessfn = access_tda, + .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, + /* + * Dummy MDCCINT_EL1, since we don't implement the Debug Communications + * Channel but Linux may try to access this register. The 32-bit + * alias is DBGDCCINT. + */ + { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, + .access = PL1_RW, .accessfn = access_tda, + .type = ARM_CP_NOP }, +}; + +static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { + /* 64 bit access versions of the (dummy) debug registers */ + { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, +}; + +void hw_watchpoint_update(ARMCPU *cpu, int n) +{ + CPUARMState *env = &cpu->env; + vaddr len = 0; + vaddr wvr = env->cp15.dbgwvr[n]; + uint64_t wcr = env->cp15.dbgwcr[n]; + int mask; + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + + if (env->cpu_watchpoint[n]) { + cpu_watchpoint_remove_by_ref(CPU(cpu), env->cpu_watchpoint[n]); + env->cpu_watchpoint[n] = NULL; + } + + if (!FIELD_EX64(wcr, DBGWCR, E)) { + /* E bit clear : watchpoint disabled */ + return; + } + + switch (FIELD_EX64(wcr, DBGWCR, LSC)) { + case 0: + /* LSC 00 is reserved and must behave as if the wp is disabled */ + return; + case 1: + flags |= BP_MEM_READ; + break; + case 2: + flags |= BP_MEM_WRITE; + break; + case 3: + flags |= BP_MEM_ACCESS; + break; + } + + /* + * Attempts to use both MASK and BAS fields simultaneously are + * CONSTRAINED UNPREDICTABLE; we opt to ignore BAS in this case, + * thus generating a watchpoint for every byte in the masked region. + */ + mask = FIELD_EX64(wcr, DBGWCR, MASK); + if (mask == 1 || mask == 2) { + /* + * Reserved values of MASK; we must act as if the mask value was + * some non-reserved value, or as if the watchpoint were disabled. + * We choose the latter. + */ + return; + } else if (mask) { + /* Watchpoint covers an aligned area up to 2GB in size */ + len = 1ULL << mask; + /* + * If masked bits in WVR are not zero it's CONSTRAINED UNPREDICTABLE + * whether the watchpoint fires when the unmasked bits match; we opt + * to generate the exceptions. + */ + wvr &= ~(len - 1); + } else { + /* Watchpoint covers bytes defined by the byte address select bits */ + int bas = FIELD_EX64(wcr, DBGWCR, BAS); + int basstart; + + if (extract64(wvr, 2, 1)) { + /* + * Deprecated case of an only 4-aligned address. BAS[7:4] are + * ignored, and BAS[3:0] define which bytes to watch. + */ + bas &= 0xf; + } + + if (bas == 0) { + /* This must act as if the watchpoint is disabled */ + return; + } + + /* + * The BAS bits are supposed to be programmed to indicate a contiguous + * range of bytes. Otherwise it is CONSTRAINED UNPREDICTABLE whether + * we fire for each byte in the word/doubleword addressed by the WVR. + * We choose to ignore any non-zero bits after the first range of 1s. + */ + basstart = ctz32(bas); + len = cto32(bas >> basstart); + wvr += basstart; + } + + cpu_watchpoint_insert(CPU(cpu), wvr, len, flags, + &env->cpu_watchpoint[n]); +} + +void hw_watchpoint_update_all(ARMCPU *cpu) +{ + int i; + CPUARMState *env = &cpu->env; + + /* + * Completely clear out existing QEMU watchpoints and our array, to + * avoid possible stale entries following migration load. + */ + cpu_watchpoint_remove_all(CPU(cpu), BP_CPU); + memset(env->cpu_watchpoint, 0, sizeof(env->cpu_watchpoint)); + + for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_watchpoint); i++) { + hw_watchpoint_update(cpu, i); + } +} + +static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + /* + * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether [63:49] ([63:53] with FEAT_LVA) + * are hardwired to the value of bit [48] ([52] with FEAT_LVA), or if + * they contain the value written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * + * Therefore we are allowed to compare the entire register, which lets + * us avoid considering whether or not FEAT_LVA is actually enabled. + */ + value &= ~3ULL; + + raw_write(env, ri, value); + hw_watchpoint_update(cpu, i); +} + +static void dbgwcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + raw_write(env, ri, value); + hw_watchpoint_update(cpu, i); +} + +void hw_breakpoint_update(ARMCPU *cpu, int n) +{ + CPUARMState *env = &cpu->env; + uint64_t bvr = env->cp15.dbgbvr[n]; + uint64_t bcr = env->cp15.dbgbcr[n]; + vaddr addr; + int bt; + int flags = BP_CPU; + + if (env->cpu_breakpoint[n]) { + cpu_breakpoint_remove_by_ref(CPU(cpu), env->cpu_breakpoint[n]); + env->cpu_breakpoint[n] = NULL; + } + + if (!extract64(bcr, 0, 1)) { + /* E bit clear : watchpoint disabled */ + return; + } + + bt = extract64(bcr, 20, 4); + + switch (bt) { + case 4: /* unlinked address mismatch (reserved if AArch64) */ + case 5: /* linked address mismatch (reserved if AArch64) */ + qemu_log_mask(LOG_UNIMP, + "arm: address mismatch breakpoint types not implemented\n"); + return; + case 0: /* unlinked address match */ + case 1: /* linked address match */ + { + /* + * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether bits [63:49] + * ([63:53] for FEAT_LVA) are hardwired to a copy of the sign bit + * of the VA field ([48] or [52] for FEAT_LVA), or whether the + * value is read as written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * Therefore we are allowed to compare the entire register, which + * lets us avoid considering whether FEAT_LVA is actually enabled. + * + * The BAS field is used to allow setting breakpoints on 16-bit + * wide instructions; it is CONSTRAINED UNPREDICTABLE whether + * a bp will fire if the addresses covered by the bp and the addresses + * covered by the insn overlap but the insn doesn't start at the + * start of the bp address range. We choose to require the insn and + * the bp to have the same address. The constraints on writing to + * BAS enforced in dbgbcr_write mean we have only four cases: + * 0b0000 => no breakpoint + * 0b0011 => breakpoint on addr + * 0b1100 => breakpoint on addr + 2 + * 0b1111 => breakpoint on addr + * See also figure D2-3 in the v8 ARM ARM (DDI0487A.c). + */ + int bas = extract64(bcr, 5, 4); + addr = bvr & ~3ULL; + if (bas == 0) { + return; + } + if (bas == 0xc) { + addr += 2; + } + break; + } + case 2: /* unlinked context ID match */ + case 8: /* unlinked VMID match (reserved if no EL2) */ + case 10: /* unlinked context ID and VMID match (reserved if no EL2) */ + qemu_log_mask(LOG_UNIMP, + "arm: unlinked context breakpoint types not implemented\n"); + return; + case 9: /* linked VMID match (reserved if no EL2) */ + case 11: /* linked context ID and VMID match (reserved if no EL2) */ + case 3: /* linked context ID match */ + default: + /* + * We must generate no events for Linked context matches (unless + * they are linked to by some other bp/wp, which is handled in + * updates for the linking bp/wp). We choose to also generate no events + * for reserved values. + */ + return; + } + + cpu_breakpoint_insert(CPU(cpu), addr, flags, &env->cpu_breakpoint[n]); +} + +void hw_breakpoint_update_all(ARMCPU *cpu) +{ + int i; + CPUARMState *env = &cpu->env; + + /* + * Completely clear out existing QEMU breakpoints and our array, to + * avoid possible stale entries following migration load. + */ + cpu_breakpoint_remove_all(CPU(cpu), BP_CPU); + memset(env->cpu_breakpoint, 0, sizeof(env->cpu_breakpoint)); + + for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_breakpoint); i++) { + hw_breakpoint_update(cpu, i); + } +} + +static void dbgbvr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + raw_write(env, ri, value); + hw_breakpoint_update(cpu, i); +} + +static void dbgbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + /* + * BAS[3] is a read-only copy of BAS[2], and BAS[1] a read-only + * copy of BAS[0]. + */ + value = deposit64(value, 6, 1, extract64(value, 5, 1)); + value = deposit64(value, 8, 1, extract64(value, 7, 1)); + + raw_write(env, ri, value); + hw_breakpoint_update(cpu, i); +} + +void define_debug_regs(ARMCPU *cpu) +{ + /* + * Define v7 and v8 architectural debug registers. + * These are just dummy implementations for now. + */ + int i; + int wrps, brps, ctx_cmps; + + /* + * The Arm ARM says DBGDIDR is optional and deprecated if EL1 cannot + * use AArch32. Given that bit 15 is RES1, if the value is 0 then + * the register must not exist for this cpu. + */ + if (cpu->isar.dbgdidr != 0) { + ARMCPRegInfo dbgdidr = { + .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, + .opc1 = 0, .opc2 = 0, + .access = PL0_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdidr, + }; + define_one_arm_cp_reg(cpu, &dbgdidr); + } + + /* + * DBGDEVID is present in the v7 debug architecture if + * DBGDIDR.DEVID_imp is 1 (bit 15); from v7.1 and on it is + * mandatory (and bit 15 is RES1). DBGDEVID1 and DBGDEVID2 exist + * from v7.1 of the debug architecture. Because no fields have yet + * been defined in DBGDEVID2 (and quite possibly none will ever + * be) we don't define an ARMISARegisters field for it. + * These registers exist only if EL1 can use AArch32, but that + * happens naturally because they are only PL1 accessible anyway. + */ + if (extract32(cpu->isar.dbgdidr, 15, 1)) { + ARMCPRegInfo dbgdevid = { + .name = "DBGDEVID", + .cp = 14, .opc1 = 0, .crn = 7, .opc2 = 2, .crn = 7, + .access = PL1_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdevid, + }; + define_one_arm_cp_reg(cpu, &dbgdevid); + } + if (cpu_isar_feature(aa32_debugv7p1, cpu)) { + ARMCPRegInfo dbgdevid12[] = { + { + .name = "DBGDEVID1", + .cp = 14, .opc1 = 0, .crn = 7, .opc2 = 1, .crn = 7, + .access = PL1_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdevid1, + }, { + .name = "DBGDEVID2", + .cp = 14, .opc1 = 0, .crn = 7, .opc2 = 0, .crn = 7, + .access = PL1_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0, + }, + }; + define_arm_cp_regs(cpu, dbgdevid12); + } + + brps = arm_num_brps(cpu); + wrps = arm_num_wrps(cpu); + ctx_cmps = arm_num_ctx_cmps(cpu); + + assert(ctx_cmps <= brps); + + define_arm_cp_regs(cpu, debug_cp_reginfo); + + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { + define_arm_cp_regs(cpu, debug_lpae_cp_reginfo); + } + + for (i = 0; i < brps; i++) { + char *dbgbvr_el1_name = g_strdup_printf("DBGBVR%d_EL1", i); + char *dbgbcr_el1_name = g_strdup_printf("DBGBCR%d_EL1", i); + ARMCPRegInfo dbgregs[] = { + { .name = dbgbvr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, + .access = PL1_RW, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), + .writefn = dbgbvr_write, .raw_writefn = raw_write + }, + { .name = dbgbcr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, + .access = PL1_RW, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), + .writefn = dbgbcr_write, .raw_writefn = raw_write + }, + }; + define_arm_cp_regs(cpu, dbgregs); + g_free(dbgbvr_el1_name); + g_free(dbgbcr_el1_name); + } + + for (i = 0; i < wrps; i++) { + char *dbgwvr_el1_name = g_strdup_printf("DBGWVR%d_EL1", i); + char *dbgwcr_el1_name = g_strdup_printf("DBGWCR%d_EL1", i); + ARMCPRegInfo dbgregs[] = { + { .name = dbgwvr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, + .access = PL1_RW, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), + .writefn = dbgwvr_write, .raw_writefn = raw_write + }, + { .name = dbgwcr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, + .access = PL1_RW, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), + .writefn = dbgwcr_write, .raw_writefn = raw_write + }, + }; + define_arm_cp_regs(cpu, dbgregs); + g_free(dbgwvr_el1_name); + g_free(dbgwcr_el1_name); + } +} + #if !defined(CONFIG_USER_ONLY) vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) diff --git a/target/arm/helper-sme.h b/target/arm/helper-sme.h new file mode 100644 index 00000000000..d2d544a6961 --- /dev/null +++ b/target/arm/helper-sme.h @@ -0,0 +1,147 @@ +/* + * AArch64 SME specific helper definitions + * + * Copyright (c) 2022 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +DEF_HELPER_FLAGS_2(set_pstate_sm, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_2(set_pstate_za, TCG_CALL_NO_RWG, void, env, i32) + +DEF_HELPER_FLAGS_3(sme_zero, TCG_CALL_NO_RWG, void, env, i32, i32) + +/* Move to/from vertical array slices, i.e. columns, so 'c'. */ +DEF_HELPER_FLAGS_4(sme_mova_cz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_addha_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_addva_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_addha_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_bfmopa, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_umopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_sumopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_usmopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_smopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_umopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_sumopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_usmopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index dc629f851a3..cc4e1d89481 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -325,6 +325,8 @@ DEF_HELPER_FLAGS_5(sve_sel_zpzz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_sel_zpzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_q, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_addp_zpzz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -717,6 +719,8 @@ DEF_HELPER_FLAGS_4(sve_revh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_revw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_revd_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_rbit_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_rbit_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6457e6301cd..1a8b06410e6 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -51,8 +51,7 @@ static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) } } -static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { assert(ri->fieldoffset); if (cpreg_field_is_64bit(ri)) { @@ -302,71 +301,6 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_TRAP_UNCATEGORIZED; } -static uint64_t arm_mdcr_el2_eff(CPUARMState *env) -{ - return arm_is_el2_enabled(env) ? env->cp15.mdcr_el2 : 0; -} - -/* Check for traps to "powerdown debug" registers, which are controlled - * by MDCR.TDOSA - */ -static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - bool mdcr_el2_tdosa = (mdcr_el2 & MDCR_TDOSA) || (mdcr_el2 & MDCR_TDE) || - (arm_hcr_el2_eff(env) & HCR_TGE); - - if (el < 2 && mdcr_el2_tdosa) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - -/* Check for traps to "debug ROM" registers, which are controlled - * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. - */ -static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - bool mdcr_el2_tdra = (mdcr_el2 & MDCR_TDRA) || (mdcr_el2 & MDCR_TDE) || - (arm_hcr_el2_eff(env) & HCR_TGE); - - if (el < 2 && mdcr_el2_tdra) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - -/* Check for traps to general debug registers, which are controlled - * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. - */ -static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || - (arm_hcr_el2_eff(env) & HCR_TGE); - - if (el < 2 && mdcr_el2_tda) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - /* Check for traps to performance monitor registers, which are controlled * by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3. */ @@ -3672,19 +3606,21 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c6_region[7]) }, }; -static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - TCR *tcr = raw_ptr(env, ri); - int maskshift = extract32(value, 0, 3); + ARMCPU *cpu = env_archcpu(env); if (!arm_feature(env, ARM_FEATURE_V8)) { if (arm_feature(env, ARM_FEATURE_LPAE) && (value & TTBCR_EAE)) { - /* Pre ARMv8 bits [21:19], [15:14] and [6:3] are UNK/SBZP when - * using Long-desciptor translation table format */ + /* + * Pre ARMv8 bits [21:19], [15:14] and [6:3] are UNK/SBZP when + * using Long-descriptor translation table format + */ value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); } else if (arm_feature(env, ARM_FEATURE_EL3)) { - /* In an implementation that includes the Security Extensions + /* + * In an implementation that includes the Security Extensions * TTBCR has additional fields PD0 [4] and PD1 [5] for * Short-descriptor translation table format. */ @@ -3694,55 +3630,23 @@ static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, } } - /* Update the masks corresponding to the TCR bank being written - * Note that we always calculate mask and base_mask, but - * they are only used for short-descriptor tables (ie if EAE is 0); - * for long-descriptor tables the TCR fields are used differently - * and the mask and base_mask values are meaningless. - */ - tcr->raw_tcr = value; - tcr->mask = ~(((uint32_t)0xffffffffu) >> maskshift); - tcr->base_mask = ~((uint32_t)0x3fffu >> maskshift); -} - -static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - TCR *tcr = raw_ptr(env, ri); - if (arm_feature(env, ARM_FEATURE_LPAE)) { /* With LPAE the TTBCR could result in a change of ASID * via the TTBCR.A1 bit, so do a TLB flush. */ tlb_flush(CPU(cpu)); } - /* Preserve the high half of TCR_EL1, set via TTBCR2. */ - value = deposit64(tcr->raw_tcr, 0, 32, value); - vmsa_ttbcr_raw_write(env, ri, value); -} - -static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) -{ - TCR *tcr = raw_ptr(env, ri); - - /* Reset both the TCR as well as the masks corresponding to the bank of - * the TCR being reset. - */ - tcr->raw_tcr = 0; - tcr->mask = 0; - tcr->base_mask = 0xffffc000u; + raw_write(env, ri, value); } static void vmsa_tcr_el12_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); - TCR *tcr = raw_ptr(env, ri); /* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */ tlb_flush(CPU(cpu)); - tcr->raw_tcr = value; + raw_write(env, ri, value); } static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3846,15 +3750,15 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tvm_trvm, .writefn = vmsa_tcr_el12_write, - .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, + .raw_writefn = raw_write, + .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) }, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tvm_trvm, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, - .raw_writefn = vmsa_ttbcr_raw_write, - /* No offsetoflow32 -- pass the entire TCR to writefn/raw_writefn. */ - .bank_fieldoffsets = { offsetof(CPUARMState, cp15.tcr_el[3]), - offsetof(CPUARMState, cp15.tcr_el[1])} }, + .raw_writefn = raw_write, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]), + offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, }; /* Note that unlike TTBCR, writing to TTBCR2 does not require flushing @@ -3865,8 +3769,8 @@ static const ARMCPRegInfo ttbcr2_reginfo = { .access = PL1_RW, .accessfn = access_tvm_trvm, .type = ARM_CP_ALIAS, .bank_fieldoffsets = { - offsetofhigh32(CPUARMState, cp15.tcr_el[3].raw_tcr), - offsetofhigh32(CPUARMState, cp15.tcr_el[1].raw_tcr), + offsetofhigh32(CPUARMState, cp15.tcr_el[3]), + offsetofhigh32(CPUARMState, cp15.tcr_el[1]), }, }; @@ -4282,7 +4186,7 @@ static int vae1_tlbmask(CPUARMState *env) static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, uint64_t addr) { - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint64_t tcr = regime_tcr(env, mmu_idx); int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); int select = extract64(addr, 55, 1); @@ -5469,19 +5373,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, .access = PL2_RW, .writefn = vmsa_tcr_el12_write, - /* no .raw_writefn or .resetfn needed as we never use mask/base_mask */ .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, { .name = "VTCR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .type = ARM_CP_ALIAS, .access = PL2_RW, .accessfn = access_el3_aa32ns, - .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, + .fieldoffset = offsetoflow32(CPUARMState, cp15.vtcr_el2) }, { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .access = PL2_RW, - /* no .writefn needed as this can't cause an ASID change; - * no .raw_writefn or .resetfn needed as we never use mask/base_mask - */ + /* no .writefn needed as this can't cause an ASID change */ .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, { .name = "VTTBR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 6, .crm = 2, @@ -5711,12 +5612,8 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "TCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 2, .access = PL3_RW, - /* no .writefn needed as this can't cause an ASID change; - * we must provide a .raw_writefn and .resetfn because we handle - * reset and migration for the AArch32 TTBCR(S), which might be - * using mask and base_mask. - */ - .resetfn = vmsa_ttbcr_reset, .raw_writefn = vmsa_ttbcr_raw_write, + /* no .writefn needed as this can't cause an ASID change */ + .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[3]) }, { .name = "ELR_EL3", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, @@ -5879,6 +5776,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) */ { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0), "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve }, + { K(3, 0, 1, 2, 6), K(3, 4, 1, 2, 6), K(3, 5, 1, 2, 6), + "SMCR_EL1", "SMCR_EL2", "SMCR_EL12", isar_feature_aa64_sme }, { K(3, 0, 5, 6, 0), K(3, 4, 5, 6, 0), K(3, 5, 5, 6, 0), "TFSR_EL1", "TFSR_EL2", "TFSR_EL12", isar_feature_aa64_mte }, @@ -5977,111 +5876,6 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -static void oslar_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Writes to OSLAR_EL1 may update the OS lock status, which can be - * read via a bit in OSLSR_EL1. - */ - int oslock; - - if (ri->state == ARM_CP_STATE_AA32) { - oslock = (value == 0xC5ACCE55); - } else { - oslock = value & 1; - } - - env->cp15.oslsr_el1 = deposit32(env->cp15.oslsr_el1, 1, 1, oslock); -} - -static const ARMCPRegInfo debug_cp_reginfo[] = { - /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped - * debug components. The AArch64 version of DBGDRAR is named MDRAR_EL1; - * unlike DBGDRAR it is never accessible from EL0. - * DBGDSAR is deprecated and must RAZ from v8 anyway, so it has no AArch64 - * accessor. - */ - { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, - .access = PL1_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, - /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ - { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), - .resetvalue = 0 }, - /* - * MDCCSR_EL0[30:29] map to EDSCR[30:29]. Simply RAZ as the external - * Debug Communication Channel is not implemented. - */ - { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, - .access = PL0_R, .accessfn = access_tda, - .type = ARM_CP_CONST, .resetvalue = 0 }, - /* - * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as - * it is unlikely a guest will care. - * We don't implement the configurable EL0 access. - */ - { .name = "DBGDSCRint", .state = ARM_CP_STATE_AA32, - .cp = 14, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, - .type = ARM_CP_ALIAS, - .access = PL1_R, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, - { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, - .access = PL1_W, .type = ARM_CP_NO_RAW, - .accessfn = access_tdosa, - .writefn = oslar_write }, - { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, - .access = PL1_R, .resetvalue = 10, - .accessfn = access_tdosa, - .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, - /* Dummy OSDLR_EL1: 32-bit Linux will read this */ - { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, - .access = PL1_RW, .accessfn = access_tdosa, - .type = ARM_CP_NOP }, - /* Dummy DBGVCR: Linux wants to clear this on startup, but we don't - * implement vector catch debug events yet. - */ - { .name = "DBGVCR", - .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL1_RW, .accessfn = access_tda, - .type = ARM_CP_NOP }, - /* Dummy DBGVCR32_EL2 (which is only for a 64-bit hypervisor - * to save and restore a 32-bit guest's DBGVCR) - */ - { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL2_RW, .accessfn = access_tda, - .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, - /* Dummy MDCCINT_EL1, since we don't implement the Debug Communications - * Channel but Linux may try to access this register. The 32-bit - * alias is DBGDCCINT. - */ - { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, - .access = PL1_RW, .accessfn = access_tda, - .type = ARM_CP_NOP }, -}; - -static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { - /* 64 bit access versions of the (dummy) debug registers */ - { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, - { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, -}; - /* * Check for traps to RAS registers, which are controlled * by HCR_EL2.TERR and SCR_EL3.TERR. @@ -6218,26 +6012,119 @@ int sve_exception_el(CPUARMState *env, int el) return 0; } +/* + * Return the exception level to which exceptions should be taken for SME. + * C.f. the ARM pseudocode function CheckSMEAccess. + */ +int sme_exception_el(CPUARMState *env, int el) +{ +#ifndef CONFIG_USER_ONLY + if (el <= 1 && !el_is_in_host(env, el)) { + switch (FIELD_EX64(env->cp15.cpacr_el1, CPACR_EL1, SMEN)) { + case 1: + if (el != 0) { + break; + } + /* fall through */ + case 0: + case 2: + return 1; + } + } + + if (el <= 2 && arm_is_el2_enabled(env)) { + /* CPTR_EL2 changes format with HCR_EL2.E2H (regardless of TGE). */ + if (env->cp15.hcr_el2 & HCR_E2H) { + switch (FIELD_EX64(env->cp15.cptr_el[2], CPTR_EL2, SMEN)) { + case 1: + if (el != 0 || !(env->cp15.hcr_el2 & HCR_TGE)) { + break; + } + /* fall through */ + case 0: + case 2: + return 2; + } + } else { + if (FIELD_EX64(env->cp15.cptr_el[2], CPTR_EL2, TSM)) { + return 2; + } + } + } + + /* CPTR_EL3. Since ESM is negative we must check for EL3. */ + if (arm_feature(env, ARM_FEATURE_EL3) + && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { + return 3; + } +#endif + return 0; +} + +/* This corresponds to the ARM pseudocode function IsFullA64Enabled(). */ +static bool sme_fa64(CPUARMState *env, int el) +{ + if (!cpu_isar_feature(aa64_sme_fa64, env_archcpu(env))) { + return false; + } + + if (el <= 1 && !el_is_in_host(env, el)) { + if (!FIELD_EX64(env->vfp.smcr_el[1], SMCR, FA64)) { + return false; + } + } + if (el <= 2 && arm_is_el2_enabled(env)) { + if (!FIELD_EX64(env->vfp.smcr_el[2], SMCR, FA64)) { + return false; + } + } + if (arm_feature(env, ARM_FEATURE_EL3)) { + if (!FIELD_EX64(env->vfp.smcr_el[3], SMCR, FA64)) { + return false; + } + } + + return true; +} + /* * Given that SVE is enabled, return the vector length for EL. */ -uint32_t sve_vqm1_for_el(CPUARMState *env, int el) +uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm) { ARMCPU *cpu = env_archcpu(env); - uint32_t len = cpu->sve_max_vq - 1; + uint64_t *cr = env->vfp.zcr_el; + uint32_t map = cpu->sve_vq.map; + uint32_t len = ARM_MAX_VQ - 1; + + if (sm) { + cr = env->vfp.smcr_el; + map = cpu->sme_vq.map; + } if (el <= 1 && !el_is_in_host(env, el)) { - len = MIN(len, 0xf & (uint32_t)env->vfp.zcr_el[1]); + len = MIN(len, 0xf & (uint32_t)cr[1]); } if (el <= 2 && arm_feature(env, ARM_FEATURE_EL2)) { - len = MIN(len, 0xf & (uint32_t)env->vfp.zcr_el[2]); + len = MIN(len, 0xf & (uint32_t)cr[2]); } if (arm_feature(env, ARM_FEATURE_EL3)) { - len = MIN(len, 0xf & (uint32_t)env->vfp.zcr_el[3]); + len = MIN(len, 0xf & (uint32_t)cr[3]); } - len = 31 - clz32(cpu->sve_vq_map & MAKE_64BIT_MASK(0, len + 1)); - return len; + map &= MAKE_64BIT_MASK(0, len + 1); + if (map != 0) { + return 31 - clz32(map); + } + + /* Bit 0 is always set for Normal SVE -- not so for Streaming SVE. */ + assert(sm); + return ctz32(cpu->sme_vq.map); +} + +uint32_t sve_vqm1_for_el(CPUARMState *env, int el) +{ + return sve_vqm1_for_el_sm(env, el, FIELD_EX64(env->svcr, SVCR, SM)); } static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6279,345 +6166,119 @@ static const ARMCPRegInfo zcr_reginfo[] = { .writefn = zcr_write, .raw_writefn = raw_write }, }; -void hw_watchpoint_update(ARMCPU *cpu, int n) +#ifdef TARGET_AARCH64 +static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - CPUARMState *env = &cpu->env; - vaddr len = 0; - vaddr wvr = env->cp15.dbgwvr[n]; - uint64_t wcr = env->cp15.dbgwcr[n]; - int mask; - int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; - - if (env->cpu_watchpoint[n]) { - cpu_watchpoint_remove_by_ref(CPU(cpu), env->cpu_watchpoint[n]); - env->cpu_watchpoint[n] = NULL; - } - - if (!FIELD_EX64(wcr, DBGWCR, E)) { - /* E bit clear : watchpoint disabled */ - return; - } - - switch (FIELD_EX64(wcr, DBGWCR, LSC)) { - case 0: - /* LSC 00 is reserved and must behave as if the wp is disabled */ - return; - case 1: - flags |= BP_MEM_READ; - break; - case 2: - flags |= BP_MEM_WRITE; - break; - case 3: - flags |= BP_MEM_ACCESS; - break; - } - - /* Attempts to use both MASK and BAS fields simultaneously are - * CONSTRAINED UNPREDICTABLE; we opt to ignore BAS in this case, - * thus generating a watchpoint for every byte in the masked region. - */ - mask = FIELD_EX64(wcr, DBGWCR, MASK); - if (mask == 1 || mask == 2) { - /* Reserved values of MASK; we must act as if the mask value was - * some non-reserved value, or as if the watchpoint were disabled. - * We choose the latter. - */ - return; - } else if (mask) { - /* Watchpoint covers an aligned area up to 2GB in size */ - len = 1ULL << mask; - /* If masked bits in WVR are not zero it's CONSTRAINED UNPREDICTABLE - * whether the watchpoint fires when the unmasked bits match; we opt - * to generate the exceptions. - */ - wvr &= ~(len - 1); - } else { - /* Watchpoint covers bytes defined by the byte address select bits */ - int bas = FIELD_EX64(wcr, DBGWCR, BAS); - int basstart; - - if (extract64(wvr, 2, 1)) { - /* Deprecated case of an only 4-aligned address. BAS[7:4] are - * ignored, and BAS[3:0] define which bytes to watch. - */ - bas &= 0xf; - } + int el = arm_current_el(env); - if (bas == 0) { - /* This must act as if the watchpoint is disabled */ - return; + if (el == 0) { + uint64_t sctlr = arm_sctlr(env, el); + if (!(sctlr & SCTLR_EnTP2)) { + return CP_ACCESS_TRAP; } - - /* The BAS bits are supposed to be programmed to indicate a contiguous - * range of bytes. Otherwise it is CONSTRAINED UNPREDICTABLE whether - * we fire for each byte in the word/doubleword addressed by the WVR. - * We choose to ignore any non-zero bits after the first range of 1s. - */ - basstart = ctz32(bas); - len = cto32(bas >> basstart); - wvr += basstart; } - - cpu_watchpoint_insert(CPU(cpu), wvr, len, flags, - &env->cpu_watchpoint[n]); -} - -void hw_watchpoint_update_all(ARMCPU *cpu) -{ - int i; - CPUARMState *env = &cpu->env; - - /* Completely clear out existing QEMU watchpoints and our array, to - * avoid possible stale entries following migration load. - */ - cpu_watchpoint_remove_all(CPU(cpu), BP_CPU); - memset(env->cpu_watchpoint, 0, sizeof(env->cpu_watchpoint)); - - for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_watchpoint); i++) { - hw_watchpoint_update(cpu, i); + /* TODO: FEAT_FGT */ + if (el < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_ENTP2)) { + return CP_ACCESS_TRAP_EL3; } + return CP_ACCESS_OK; } -static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; - - /* - * Bits [1:0] are RES0. - * - * It is IMPLEMENTATION DEFINED whether [63:49] ([63:53] with FEAT_LVA) - * are hardwired to the value of bit [48] ([52] with FEAT_LVA), or if - * they contain the value written. It is CONSTRAINED UNPREDICTABLE - * whether the RESS bits are ignored when comparing an address. - * - * Therefore we are allowed to compare the entire register, which lets - * us avoid considering whether or not FEAT_LVA is actually enabled. - */ - value &= ~3ULL; - - raw_write(env, ri, value); - hw_watchpoint_update(cpu, i); -} - -static void dbgwcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; - - raw_write(env, ri, value); - hw_watchpoint_update(cpu, i); -} - -void hw_breakpoint_update(ARMCPU *cpu, int n) +static CPAccessResult access_esm(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - CPUARMState *env = &cpu->env; - uint64_t bvr = env->cp15.dbgbvr[n]; - uint64_t bcr = env->cp15.dbgbcr[n]; - vaddr addr; - int bt; - int flags = BP_CPU; - - if (env->cpu_breakpoint[n]) { - cpu_breakpoint_remove_by_ref(CPU(cpu), env->cpu_breakpoint[n]); - env->cpu_breakpoint[n] = NULL; - } - - if (!extract64(bcr, 0, 1)) { - /* E bit clear : watchpoint disabled */ - return; - } - - bt = extract64(bcr, 20, 4); - - switch (bt) { - case 4: /* unlinked address mismatch (reserved if AArch64) */ - case 5: /* linked address mismatch (reserved if AArch64) */ - qemu_log_mask(LOG_UNIMP, - "arm: address mismatch breakpoint types not implemented\n"); - return; - case 0: /* unlinked address match */ - case 1: /* linked address match */ - { - /* - * Bits [1:0] are RES0. - * - * It is IMPLEMENTATION DEFINED whether bits [63:49] - * ([63:53] for FEAT_LVA) are hardwired to a copy of the sign bit - * of the VA field ([48] or [52] for FEAT_LVA), or whether the - * value is read as written. It is CONSTRAINED UNPREDICTABLE - * whether the RESS bits are ignored when comparing an address. - * Therefore we are allowed to compare the entire register, which - * lets us avoid considering whether FEAT_LVA is actually enabled. - * - * The BAS field is used to allow setting breakpoints on 16-bit - * wide instructions; it is CONSTRAINED UNPREDICTABLE whether - * a bp will fire if the addresses covered by the bp and the addresses - * covered by the insn overlap but the insn doesn't start at the - * start of the bp address range. We choose to require the insn and - * the bp to have the same address. The constraints on writing to - * BAS enforced in dbgbcr_write mean we have only four cases: - * 0b0000 => no breakpoint - * 0b0011 => breakpoint on addr - * 0b1100 => breakpoint on addr + 2 - * 0b1111 => breakpoint on addr - * See also figure D2-3 in the v8 ARM ARM (DDI0487A.c). - */ - int bas = extract64(bcr, 5, 4); - addr = bvr & ~3ULL; - if (bas == 0) { - return; - } - if (bas == 0xc) { - addr += 2; - } - break; - } - case 2: /* unlinked context ID match */ - case 8: /* unlinked VMID match (reserved if no EL2) */ - case 10: /* unlinked context ID and VMID match (reserved if no EL2) */ - qemu_log_mask(LOG_UNIMP, - "arm: unlinked context breakpoint types not implemented\n"); - return; - case 9: /* linked VMID match (reserved if no EL2) */ - case 11: /* linked context ID and VMID match (reserved if no EL2) */ - case 3: /* linked context ID match */ - default: - /* We must generate no events for Linked context matches (unless - * they are linked to by some other bp/wp, which is handled in - * updates for the linking bp/wp). We choose to also generate no events - * for reserved values. - */ - return; + /* 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)) { + return CP_ACCESS_TRAP_EL3; } - - cpu_breakpoint_insert(CPU(cpu), addr, flags, &env->cpu_breakpoint[n]); + return CP_ACCESS_OK; } -void hw_breakpoint_update_all(ARMCPU *cpu) +static void svcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - int i; - CPUARMState *env = &cpu->env; - - /* Completely clear out existing QEMU breakpoints and our array, to - * avoid possible stale entries following migration load. - */ - cpu_breakpoint_remove_all(CPU(cpu), BP_CPU); - memset(env->cpu_breakpoint, 0, sizeof(env->cpu_breakpoint)); - - for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_breakpoint); i++) { - hw_breakpoint_update(cpu, i); - } + helper_set_pstate_sm(env, FIELD_EX64(value, SVCR, SM)); + helper_set_pstate_za(env, FIELD_EX64(value, SVCR, ZA)); + arm_rebuild_hflags(env); } -static void dbgbvr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void smcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; + int cur_el = arm_current_el(env); + int old_len = sve_vqm1_for_el(env, cur_el); + int new_len; + QEMU_BUILD_BUG_ON(ARM_MAX_VQ > R_SMCR_LEN_MASK + 1); + value &= R_SMCR_LEN_MASK | R_SMCR_FA64_MASK; raw_write(env, ri, value); - hw_breakpoint_update(cpu, i); -} -static void dbgbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; - - /* BAS[3] is a read-only copy of BAS[2], and BAS[1] a read-only - * copy of BAS[0]. + /* + * Note that it is CONSTRAINED UNPREDICTABLE what happens to ZA storage + * when SVL is widened (old values kept, or zeros). Choose to keep the + * current values for simplicity. But for QEMU internals, we must still + * apply the narrower SVL to the Zregs and Pregs -- see the comment + * above aarch64_sve_narrow_vq. */ - value = deposit64(value, 6, 1, extract64(value, 5, 1)); - value = deposit64(value, 8, 1, extract64(value, 7, 1)); - - raw_write(env, ri, value); - hw_breakpoint_update(cpu, i); + new_len = sve_vqm1_for_el(env, cur_el); + if (new_len < old_len) { + aarch64_sve_narrow_vq(env, new_len + 1); + } } -static void define_debug_regs(ARMCPU *cpu) -{ - /* Define v7 and v8 architectural debug registers. - * These are just dummy implementations for now. - */ - int i; - int wrps, brps, ctx_cmps; - +static const ARMCPRegInfo sme_reginfo[] = { + { .name = "TPIDR2_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 5, + .access = PL0_RW, .accessfn = access_tpidr2, + .fieldoffset = offsetof(CPUARMState, cp15.tpidr2_el0) }, + { .name = "SVCR", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 2, + .access = PL0_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, svcr), + .writefn = svcr_write, .raw_writefn = raw_write }, + { .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6, + .access = PL1_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]), + .writefn = smcr_write, .raw_writefn = raw_write }, + { .name = "SMCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 6, + .access = PL2_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[2]), + .writefn = smcr_write, .raw_writefn = raw_write }, + { .name = "SMCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 2, .opc2 = 6, + .access = PL3_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[3]), + .writefn = smcr_write, .raw_writefn = raw_write }, + { .name = "SMIDR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 6, + .access = PL1_R, .accessfn = access_aa64_tid1, + /* + * IMPLEMENTOR = 0 (software) + * REVISION = 0 (implementation defined) + * SMPS = 0 (no streaming execution priority in QEMU) + * AFFINITY = 0 (streaming sve mode not shared with other PEs) + */ + .type = ARM_CP_CONST, .resetvalue = 0, }, /* - * The Arm ARM says DBGDIDR is optional and deprecated if EL1 cannot - * use AArch32. Given that bit 15 is RES1, if the value is 0 then - * the register must not exist for this cpu. + * Because SMIDR_EL1.SMPS is 0, SMPRI_EL1 and SMPRIMAP_EL2 are RES 0. */ - if (cpu->isar.dbgdidr != 0) { - ARMCPRegInfo dbgdidr = { - .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, - .opc1 = 0, .opc2 = 0, - .access = PL0_R, .accessfn = access_tda, - .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdidr, - }; - define_one_arm_cp_reg(cpu, &dbgdidr); - } - - brps = arm_num_brps(cpu); - wrps = arm_num_wrps(cpu); - ctx_cmps = arm_num_ctx_cmps(cpu); - - assert(ctx_cmps <= brps); - - define_arm_cp_regs(cpu, debug_cp_reginfo); - - if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { - define_arm_cp_regs(cpu, debug_lpae_cp_reginfo); - } - - for (i = 0; i < brps; i++) { - char *dbgbvr_el1_name = g_strdup_printf("DBGBVR%d_EL1", i); - char *dbgbcr_el1_name = g_strdup_printf("DBGBCR%d_EL1", i); - ARMCPRegInfo dbgregs[] = { - { .name = dbgbvr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), - .writefn = dbgbvr_write, .raw_writefn = raw_write - }, - { .name = dbgbcr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), - .writefn = dbgbcr_write, .raw_writefn = raw_write - }, - }; - define_arm_cp_regs(cpu, dbgregs); - g_free(dbgbvr_el1_name); - g_free(dbgbcr_el1_name); - } - - for (i = 0; i < wrps; i++) { - char *dbgwvr_el1_name = g_strdup_printf("DBGWVR%d_EL1", i); - char *dbgwcr_el1_name = g_strdup_printf("DBGWCR%d_EL1", i); - ARMCPRegInfo dbgregs[] = { - { .name = dbgwvr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), - .writefn = dbgwvr_write, .raw_writefn = raw_write - }, - { .name = dbgwcr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), - .writefn = dbgwcr_write, .raw_writefn = raw_write - }, - }; - define_arm_cp_regs(cpu, dbgregs); - g_free(dbgwvr_el1_name); - g_free(dbgwcr_el1_name); - } -} + { .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4, + .access = PL1_RW, .accessfn = access_esm, + .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, + .type = ARM_CP_CONST, .resetvalue = 0 }, +}; +#endif /* TARGET_AARCH64 */ static void define_pmu_regs(ARMCPU *cpu) { @@ -8440,6 +8101,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) } #ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_sme, cpu)) { + define_arm_cp_regs(cpu, sme_reginfo); + } if (cpu_isar_feature(aa64_pauth, cpu)) { define_arm_cp_regs(cpu, pauth_reginfo); } @@ -10329,13 +9993,13 @@ static void handle_semihosting(CPUState *cs) qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%" PRIx64 "\n", env->xregs[0]); - env->xregs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; } else { qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%x\n", env->regs[0]); - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->regs[15] += env->thumb ? 2 : 4; } } @@ -10457,7 +10121,7 @@ static int aa64_va_parameter_tcma(uint64_t tcr, ARMMMUIdx mmu_idx) ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ARMMMUIdx mmu_idx, bool data) { - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint64_t tcr = regime_tcr(env, mmu_idx); bool epd, hpd, using16k, using64k, tsz_oob, ds; int select, tsz, tbi, max_tsz, min_tsz, ps, sh; ARMCPU *cpu = env_archcpu(env); @@ -11126,6 +10790,20 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_ANY(flags, PSTATE__IL, 1); } + /* + * The SME exception we are testing for is raised via + * AArch64.CheckFPAdvSIMDEnabled(), as called from + * AArch32.CheckAdvSIMDOrFPEnabled(). + */ + if (el == 0 + && FIELD_EX64(env->svcr, SVCR, SM) + && (!arm_is_el2_enabled(env) + || (arm_el_is_aa64(env, 2) && !(env->cp15.hcr_el2 & HCR_TGE))) + && arm_el_is_aa64(env, 1) + && !sme_fa64(env, el)) { + DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1); + } + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } @@ -11134,7 +10812,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)->raw_tcr; + uint64_t tcr = regime_tcr(env, mmu_idx); uint64_t sctlr; int tbii, tbid; @@ -11165,6 +10843,26 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } DP_TBFLAG_A64(flags, SVEEXC_EL, sve_el); } + if (cpu_isar_feature(aa64_sme, env_archcpu(env))) { + int sme_el = sme_exception_el(env, el); + bool sm = FIELD_EX64(env->svcr, SVCR, SM); + + DP_TBFLAG_A64(flags, SMEEXC_EL, sme_el); + if (sme_el == 0) { + /* Similarly, do not compute SVL if SME is disabled. */ + int svl = sve_vqm1_for_el_sm(env, el, true); + DP_TBFLAG_A64(flags, SVL, svl); + if (sm) { + /* If SVE is disabled, we will not have set VL above. */ + DP_TBFLAG_A64(flags, VL, svl); + } + } + if (sm) { + DP_TBFLAG_A64(flags, PSTATE_SM, 1); + DP_TBFLAG_A64(flags, SME_TRAP_NONSTREAMING, !sme_fa64(env, el)); + } + DP_TBFLAG_A64(flags, PSTATE_ZA, FIELD_EX64(env->svcr, SVCR, ZA)); + } sctlr = regime_sctlr(env, stage1); @@ -11493,6 +11191,21 @@ void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) } } +static uint32_t sve_vqm1_for_el_sm_ena(CPUARMState *env, int el, bool sm) +{ + int exc_el; + + if (sm) { + exc_el = sme_exception_el(env, el); + } else { + exc_el = sve_exception_el(env, el); + } + if (exc_el) { + return 0; /* disabled */ + } + return sve_vqm1_for_el_sm(env, el, sm); +} + /* * Notice a change in SVE vector size when changing EL. */ @@ -11501,7 +11214,7 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, { ARMCPU *cpu = env_archcpu(env); int old_len, new_len; - bool old_a64, new_a64; + bool old_a64, new_a64, sm; /* Nothing to do if no SVE. */ if (!cpu_isar_feature(aa64_sve, cpu)) { @@ -11513,6 +11226,20 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, return; } + old_a64 = old_el ? arm_el_is_aa64(env, old_el) : el0_a64; + new_a64 = new_el ? arm_el_is_aa64(env, new_el) : el0_a64; + + /* + * Both AArch64.TakeException and AArch64.ExceptionReturn + * invoke ResetSVEState when taking an exception from, or + * returning to, AArch32 state when PSTATE.SM is enabled. + */ + sm = FIELD_EX64(env->svcr, SVCR, SM); + if (old_a64 != new_a64 && sm) { + arm_reset_sve_state(env); + return; + } + /* * DDI0584A.d sec 3.2: "If SVE instructions are disabled or trapped * at ELx, or not available because the EL is in AArch32 state, then @@ -11525,12 +11252,13 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, * we already have the correct register contents when encountering the * vq0->vq0 transition between EL0->EL1. */ - old_a64 = old_el ? arm_el_is_aa64(env, old_el) : el0_a64; - old_len = (old_a64 && !sve_exception_el(env, old_el) - ? sve_vqm1_for_el(env, old_el) : 0); - new_a64 = new_el ? arm_el_is_aa64(env, new_el) : el0_a64; - new_len = (new_a64 && !sve_exception_el(env, new_el) - ? sve_vqm1_for_el(env, new_el) : 0); + old_len = new_len = 0; + if (old_a64) { + old_len = sve_vqm1_for_el_sm_ena(env, old_el, sm); + } + if (new_a64) { + new_len = sve_vqm1_for_el_sm_ena(env, new_el, sm); + } /* When changing vector length, clear inaccessible state. */ if (new_len < old_len) { diff --git a/target/arm/helper.h b/target/arm/helper.h index 07d45faf495..92f36d9dbb7 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1019,9 +1019,28 @@ DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_uclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #include "helper-sve.h" +#include "helper-sme.h" #endif #include "helper-mve.h" diff --git a/target/arm/internals.h b/target/arm/internals.h index 6f94f3019db..b8fefdff675 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -252,9 +252,9 @@ unsigned int arm_pamax(ARMCPU *cpu); */ static inline bool extended_addresses_enabled(CPUARMState *env) { - TCR *tcr = &env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; + uint64_t tcr = env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; return arm_el_is_aa64(env, 1) || - (arm_feature(env, ARM_FEATURE_LPAE) && (tcr->raw_tcr & TTBCR_EAE)); + (arm_feature(env, ARM_FEATURE_LPAE) && (tcr & TTBCR_EAE)); } /* Update a QEMU watchpoint based on the information the guest has set in the @@ -777,20 +777,36 @@ static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) return env->cp15.sctlr_el[regime_el(env, mmu_idx)]; } -/* Return the TCR controlling this translation regime */ -static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) +/* + * These are the fields in VTCR_EL2 which affect both the Secure stage 2 + * and the Non-Secure stage 2 translation regimes (and hence which are + * not present in VSTCR_EL2). + */ +#define VTCR_SHARED_FIELD_MASK \ + (R_VTCR_IRGN0_MASK | R_VTCR_ORGN0_MASK | R_VTCR_SH0_MASK | \ + R_VTCR_PS_MASK | R_VTCR_VS_MASK | R_VTCR_HA_MASK | R_VTCR_HD_MASK | \ + R_VTCR_DS_MASK) + +/* Return the value of the TCR controlling this translation regime */ +static inline uint64_t regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { if (mmu_idx == ARMMMUIdx_Stage2) { - return &env->cp15.vtcr_el2; + return env->cp15.vtcr_el2; } if (mmu_idx == ARMMMUIdx_Stage2_S) { /* - * Note: Secure stage 2 nominally shares fields from VTCR_EL2, but - * those are not currently used by QEMU, so just return VSTCR_EL2. + * Secure stage 2 shares fields from VTCR_EL2. We merge those + * in with the VSTCR_EL2 value to synthesize a single VTCR_EL2 format + * value so the callers don't need to special case this. + * + * If a future architecture change defines bits in VSTCR_EL2 that + * overlap with these VTCR_EL2 fields we may need to revisit this. */ - return &env->cp15.vstcr_el2; + uint64_t v = env->cp15.vstcr_el2 & ~VTCR_SHARED_FIELD_MASK; + v |= env->cp15.vtcr_el2 & VTCR_SHARED_FIELD_MASK; + return v; } - return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; + return env->cp15.tcr_el[regime_el(env, mmu_idx)]; } /** @@ -1288,6 +1304,10 @@ int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg); int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg); int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg); int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg); +void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp); #endif #ifdef CONFIG_USER_ONLY @@ -1303,6 +1323,15 @@ int exception_target_el(CPUARMState *env); bool arm_singlestep_active(CPUARMState *env); bool arm_generate_debug_exceptions(CPUARMState *env); +/* Add the cpreg definitions for debug related system registers */ +void define_debug_regs(ARMCPU *cpu); + +/* Effective value of MDCR_EL2 */ +static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) +{ + return arm_is_el2_enabled(env) ? env->cp15.mdcr_el2 : 0; +} + /* Powers of 2 for sve_vq_map et al. */ #define SVE_VQ_POW2_MAP \ ((1 << (1 - 1)) | (1 << (2 - 1)) | \ diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index ff8f65da22f..d16d4ea2500 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -820,7 +820,7 @@ uint32_t kvm_arm_sve_get_vls(CPUState *cs) 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 }; + 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], diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index a740c3e160f..308610f6b4e 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -2373,7 +2373,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) "...handling as semihosting call 0x%x\n", env->regs[0]); #ifdef CONFIG_TCG - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); #else g_assert_not_reached(); #endif diff --git a/target/arm/machine.c b/target/arm/machine.c index 285e387d2c3..54c5c62433d 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -167,6 +167,39 @@ static const VMStateDescription vmstate_sve = { VMSTATE_END_OF_LIST() } }; + +static const VMStateDescription vmstate_vreg = { + .name = "vreg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(d, ARMVectorReg, ARM_MAX_VQ * 2), + VMSTATE_END_OF_LIST() + } +}; + +static bool za_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + + /* + * When ZA storage is disabled, its contents are discarded. + * It will be zeroed when ZA storage is re-enabled. + */ + return FIELD_EX64(cpu->env.svcr, SVCR, ZA); +} + +static const VMStateDescription vmstate_za = { + .name = "cpu/sme", + .version_id = 1, + .minimum_version_id = 1, + .needed = za_needed, + .fields = (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) @@ -884,6 +917,7 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_m_security, #ifdef TARGET_AARCH64 &vmstate_sve, + &vmstate_za, #endif &vmstate_serror, &vmstate_irq_line_state, diff --git a/target/arm/meson.build b/target/arm/meson.build index ac571fc45db..87e911b27fb 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -1,5 +1,7 @@ gen = [ decodetree.process('sve.decode', extra_args: '--decode=disas_sve'), + decodetree.process('sme.decode', extra_args: '--decode=disas_sme'), + decodetree.process('sme-fa64.decode', extra_args: '--static-decode=disas_sme_fa64'), decodetree.process('neon-shared.decode', extra_args: '--decode=disas_neon_shared'), decodetree.process('neon-dp.decode', extra_args: '--decode=disas_neon_dp'), decodetree.process('neon-ls.decode', extra_args: '--decode=disas_neon_ls'), @@ -47,8 +49,10 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'mte_helper.c', 'pauth_helper.c', 'sve_helper.c', + 'sme_helper.c', 'translate-a64.c', 'translate-sve.c', + 'translate-sme.c', )) arm_softmmu_ss = ss.source_set() diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 4d97a248084..3261039d93a 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -36,15 +36,29 @@ static const uint8_t pamax_map[] = { /* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ unsigned int arm_pamax(ARMCPU *cpu) { - unsigned int parange = - FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + unsigned int parange = + FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + + /* + * id_aa64mmfr0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. + */ + assert(parange < ARRAY_SIZE(pamax_map)); + return pamax_map[parange]; + } /* - * id_aa64mmfr0 is a read-only register so values outside of the - * supported mappings can be considered an implementation error. + * 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. */ - assert(parange < ARRAY_SIZE(pamax_map)); - return pamax_map[parange]; + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE) || + arm_feature(&cpu->env, ARM_FEATURE_V7VE)) { + /* v7 with LPAE */ + return 40; + } + /* Anything else */ + return 32; } /* @@ -227,9 +241,9 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, if (arm_is_secure_below_el3(env)) { /* Check if page table walk is to secure or non-secure PA space. */ if (*is_secure) { - *is_secure = !(env->cp15.vstcr_el2.raw_tcr & VSTCR_SW); + *is_secure = !(env->cp15.vstcr_el2 & VSTCR_SW); } else { - *is_secure = !(env->cp15.vtcr_el2.raw_tcr & VTCR_NSW); + *is_secure = !(env->cp15.vtcr_el2 & VTCR_NSW); } } else { assert(!*is_secure); @@ -301,20 +315,24 @@ static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, uint32_t *table, uint32_t address) { /* Note that we can only get here for an AArch32 PL0/PL1 lookup */ - TCR *tcr = regime_tcr(env, mmu_idx); + uint64_t tcr = regime_tcr(env, mmu_idx); + int maskshift = extract32(tcr, 0, 3); + uint32_t mask = ~(((uint32_t)0xffffffffu) >> maskshift); + uint32_t base_mask; - if (address & tcr->mask) { - if (tcr->raw_tcr & TTBCR_PD1) { + if (address & mask) { + if (tcr & TTBCR_PD1) { /* Translation table walk disabled for TTBR1 */ return false; } *table = regime_ttbr(env, mmu_idx, 1) & 0xffffc000; } else { - if (tcr->raw_tcr & TTBCR_PD0) { + if (tcr & TTBCR_PD0) { /* Translation table walk disabled for TTBR0 */ return false; } - *table = regime_ttbr(env, mmu_idx, 0) & tcr->base_mask; + base_mask = ~((uint32_t)0x3fffu >> maskshift); + *table = regime_ttbr(env, mmu_idx, 0) & base_mask; } *table |= (address >> 18) & 0x3ffc; return true; @@ -806,7 +824,7 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, ARMMMUIdx mmu_idx) { - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint64_t tcr = regime_tcr(env, mmu_idx); uint32_t el = regime_el(env, mmu_idx); int select, tsz; bool epd, hpd; @@ -980,7 +998,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, uint32_t attrs; int32_t stride; int addrsize, inputsize, outputsize; - TCR *tcr = regime_tcr(env, mmu_idx); + uint64_t tcr = regime_tcr(env, mmu_idx); int ap, ns, xn, pxn; uint32_t el = regime_el(env, mmu_idx); uint64_t descaddrmask; @@ -1098,8 +1116,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * For stage 2 translations the starting level is specified by the * VTCR_EL2.SL0 field (whose interpretation depends on the page size) */ - uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2); - uint32_t sl2 = extract64(tcr->raw_tcr, 33, 1); + uint32_t sl0 = extract32(tcr, 6, 2); + uint32_t sl2 = extract64(tcr, 33, 1); uint32_t startlevel; bool ok; @@ -1243,7 +1261,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * clear the lower bits here before ORing in the low vaddr bits. */ page_size = (1ULL << ((stride * (4 - level)) + 3)); - descaddr &= ~(page_size - 1); + descaddr &= ~(hwaddr)(page_size - 1); descaddr |= (address & (page_size - 1)); /* Extract attributes from the descriptor */ attrs = extract64(descriptor, 2, 10) @@ -2323,9 +2341,9 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, ipa_secure = attrs->secure; if (arm_is_secure_below_el3(env)) { if (ipa_secure) { - attrs->secure = !(env->cp15.vstcr_el2.raw_tcr & VSTCR_SW); + attrs->secure = !(env->cp15.vstcr_el2 & VSTCR_SW); } else { - attrs->secure = !(env->cp15.vtcr_el2.raw_tcr & VTCR_NSW); + attrs->secure = !(env->cp15.vtcr_el2 & VTCR_NSW); } } else { assert(!ipa_secure); @@ -2367,11 +2385,11 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, if (arm_is_secure_below_el3(env)) { if (ipa_secure) { attrs->secure = - !(env->cp15.vstcr_el2.raw_tcr & (VSTCR_SA | VSTCR_SW)); + !(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW)); } else { attrs->secure = - !((env->cp15.vtcr_el2.raw_tcr & (VTCR_NSA | VTCR_NSW)) - || (env->cp15.vstcr_el2.raw_tcr & (VSTCR_SA | VSTCR_SW))); + !((env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW)) + || (env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW))); } } return 0; @@ -2448,7 +2466,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, int r_el = regime_el(env, mmu_idx); if (arm_el_is_aa64(env, r_el)) { int pamax = arm_pamax(env_archcpu(env)); - uint64_t tcr = env->cp15.tcr_el[r_el].raw_tcr; + uint64_t tcr = env->cp15.tcr_el[r_el]; int addrtop, tbi; tbi = aa64_va_parameter_tbi(tcr, mmu_idx); diff --git a/target/arm/sme-fa64.decode b/target/arm/sme-fa64.decode new file mode 100644 index 00000000000..47708ccc8da --- /dev/null +++ b/target/arm/sme-fa64.decode @@ -0,0 +1,60 @@ +# AArch64 SME allowed instruction decoding +# +# Copyright (c) 2022 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . + +# +# This file is processed by scripts/decodetree.py +# + +# These patterns are taken from Appendix E1.1 of DDI0616 A.a, +# Arm Architecture Reference Manual Supplement, +# The Scalable Matrix Extension (SME), for Armv9-A + +{ + [ + OK 0-00 1110 0000 0001 0010 11-- ---- ---- # SMOV W|Xd,Vn.B[0] + OK 0-00 1110 0000 0010 0010 11-- ---- ---- # SMOV W|Xd,Vn.H[0] + OK 0100 1110 0000 0100 0010 11-- ---- ---- # SMOV Xd,Vn.S[0] + OK 0000 1110 0000 0001 0011 11-- ---- ---- # UMOV Wd,Vn.B[0] + OK 0000 1110 0000 0010 0011 11-- ---- ---- # UMOV Wd,Vn.H[0] + OK 0000 1110 0000 0100 0011 11-- ---- ---- # UMOV Wd,Vn.S[0] + OK 0100 1110 0000 1000 0011 11-- ---- ---- # UMOV Xd,Vn.D[0] + ] + FAIL 0--0 111- ---- ---- ---- ---- ---- ---- # Advanced SIMD vector operations +} + +{ + [ + OK 0101 1110 --1- ---- 11-1 11-- ---- ---- # FMULX/FRECPS/FRSQRTS (scalar) + OK 0101 1110 -10- ---- 00-1 11-- ---- ---- # FMULX/FRECPS/FRSQRTS (scalar, FP16) + OK 01-1 1110 1-10 0001 11-1 10-- ---- ---- # FRECPE/FRSQRTE/FRECPX (scalar) + OK 01-1 1110 1111 1001 11-1 10-- ---- ---- # FRECPE/FRSQRTE/FRECPX (scalar, FP16) + ] + FAIL 01-1 111- ---- ---- ---- ---- ---- ---- # Advanced SIMD single-element operations +} + +FAIL 0-00 110- ---- ---- ---- ---- ---- ---- # Advanced SIMD structure load/store +FAIL 1100 1110 ---- ---- ---- ---- ---- ---- # Advanced SIMD cryptography extensions +FAIL 0001 1110 0111 1110 0000 00-- ---- ---- # FJCVTZS + +# These are the "avoidance of doubt" final table of Illegal Advanced SIMD instructions +# We don't actually need to include these, as the default is OK. +# -001 111- ---- ---- ---- ---- ---- ---- # Scalar floating-point operations +# --10 110- ---- ---- ---- ---- ---- ---- # Load/store pair of FP registers +# --01 1100 ---- ---- ---- ---- ---- ---- # Load FP register (PC-relative literal) +# --11 1100 --0- ---- ---- ---- ---- ---- # Load/store FP register (unscaled imm) +# --11 1100 --1- ---- ---- ---- ---- --10 # Load/store FP register (register offset) +# --11 1101 ---- ---- ---- ---- ---- ---- # Load/store FP register (scaled imm) diff --git a/target/arm/sme.decode b/target/arm/sme.decode new file mode 100644 index 00000000000..628804e37a8 --- /dev/null +++ b/target/arm/sme.decode @@ -0,0 +1,88 @@ +# AArch64 SME instruction descriptions +# +# Copyright (c) 2022 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . + +# +# This file is processed by scripts/decodetree.py +# + +### SME Misc + +ZERO 11000000 00 001 00000000000 imm:8 + +### SME Move into/from Array + +%mova_rs 13:2 !function=plus_12 +&mova esz rs pg zr za_imm v:bool to_vec:bool + +MOVA 11000000 esz:2 00000 0 v:1 .. pg:3 zr:5 0 za_imm:4 \ + &mova to_vec=0 rs=%mova_rs +MOVA 11000000 11 00000 1 v:1 .. pg:3 zr:5 0 za_imm:4 \ + &mova to_vec=0 rs=%mova_rs esz=4 + +MOVA 11000000 esz:2 00001 0 v:1 .. pg:3 0 za_imm:4 zr:5 \ + &mova to_vec=1 rs=%mova_rs +MOVA 11000000 11 00001 1 v:1 .. pg:3 0 za_imm:4 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=4 + +### SME Memory + +&ldst esz rs pg rn rm za_imm v:bool st:bool + +LDST1 1110000 0 esz:2 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ + &ldst rs=%mova_rs +LDST1 1110000 111 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ + &ldst esz=4 rs=%mova_rs + +&ldstr rv rn imm +@ldstr ....... ... . ...... .. ... rn:5 . imm:4 \ + &ldstr rv=%mova_rs + +LDR 1110000 100 0 000000 .. 000 ..... 0 .... @ldstr +STR 1110000 100 1 000000 .. 000 ..... 0 .... @ldstr + +### SME Add Vector to Array + +&adda zad zn pm pn +@adda_32 ........ .. ..... . pm:3 pn:3 zn:5 ... zad:2 &adda +@adda_64 ........ .. ..... . pm:3 pn:3 zn:5 .. zad:3 &adda + +ADDHA_s 11000000 10 01000 0 ... ... ..... 000 .. @adda_32 +ADDVA_s 11000000 10 01000 1 ... ... ..... 000 .. @adda_32 +ADDHA_d 11000000 11 01000 0 ... ... ..... 00 ... @adda_64 +ADDVA_d 11000000 11 01000 1 ... ... ..... 00 ... @adda_64 + +### SME Outer Product + +&op zad zn zm pm pn sub:bool +@op_32 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 .. zad:2 &op +@op_64 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 . zad:3 &op + +FMOPA_s 10000000 100 ..... ... ... ..... . 00 .. @op_32 +FMOPA_d 10000000 110 ..... ... ... ..... . 0 ... @op_64 + +BFMOPA 10000001 100 ..... ... ... ..... . 00 .. @op_32 +FMOPA_h 10000001 101 ..... ... ... ..... . 00 .. @op_32 + +SMOPA_s 1010000 0 10 0 ..... ... ... ..... . 00 .. @op_32 +SUMOPA_s 1010000 0 10 1 ..... ... ... ..... . 00 .. @op_32 +USMOPA_s 1010000 1 10 0 ..... ... ... ..... . 00 .. @op_32 +UMOPA_s 1010000 1 10 1 ..... ... ... ..... . 00 .. @op_32 + +SMOPA_d 1010000 0 11 0 ..... ... ... ..... . 0 ... @op_64 +SUMOPA_d 1010000 0 11 1 ..... ... ... ..... . 0 ... @op_64 +USMOPA_d 1010000 1 11 0 ..... ... ... ..... . 0 ... @op_64 +UMOPA_d 1010000 1 11 1 ..... ... ... ..... . 0 ... @op_64 diff --git a/target/arm/sme_helper.c b/target/arm/sme_helper.c new file mode 100644 index 00000000000..f891306bb98 --- /dev/null +++ b/target/arm/sme_helper.c @@ -0,0 +1,1201 @@ +/* + * ARM SME Operations + * + * Copyright (c) 2022 Linaro, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "tcg/tcg-gvec-desc.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "qemu/int128.h" +#include "fpu/softfloat.h" +#include "vec_internal.h" +#include "sve_ldst_internal.h" + +/* ResetSVEState */ +void arm_reset_sve_state(CPUARMState *env) +{ + memset(env->vfp.zregs, 0, sizeof(env->vfp.zregs)); + /* Recall that FFR is stored as pregs[16]. */ + memset(env->vfp.pregs, 0, sizeof(env->vfp.pregs)); + vfp_set_fpcr(env, 0x0800009f); +} + +void helper_set_pstate_sm(CPUARMState *env, uint32_t i) +{ + if (i == FIELD_EX64(env->svcr, SVCR, SM)) { + return; + } + env->svcr ^= R_SVCR_SM_MASK; + arm_reset_sve_state(env); +} + +void helper_set_pstate_za(CPUARMState *env, uint32_t i) +{ + if (i == FIELD_EX64(env->svcr, SVCR, ZA)) { + return; + } + env->svcr ^= R_SVCR_ZA_MASK; + + /* + * ResetSMEState. + * + * SetPSTATE_ZA zeros on enable and disable. We can zero this only + * on enable: while disabled, the storage is inaccessible and the + * value does not matter. We're not saving the storage in vmstate + * when disabled either. + */ + if (i) { + memset(env->zarray, 0, sizeof(env->zarray)); + } +} + +void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl) +{ + uint32_t i; + + /* + * Special case clearing the entire ZA space. + * This falls into the CONSTRAINED UNPREDICTABLE zeroing of any + * parts of the ZA storage outside of SVL. + */ + if (imm == 0xff) { + memset(env->zarray, 0, sizeof(env->zarray)); + return; + } + + /* + * Recall that ZAnH.D[m] is spread across ZA[n+8*m], + * so each row is discontiguous within ZA[]. + */ + for (i = 0; i < svl; i++) { + if (imm & (1 << (i % 8))) { + memset(&env->zarray[i], 0, svl); + } + } +} + + +/* + * When considering the ZA storage as an array of elements of + * type T, the index within that array of the Nth element of + * a vertical slice of a tile can be calculated like this, + * regardless of the size of type T. This is because the tiles + * are interleaved, so if type T is size N bytes then row 1 of + * the tile is N rows away from row 0. The division by N to + * convert a byte offset into an array index and the multiplication + * by N to convert from vslice-index-within-the-tile to + * the index within the ZA storage cancel out. + */ +#define tile_vslice_index(i) ((i) * sizeof(ARMVectorReg)) + +/* + * When doing byte arithmetic on the ZA storage, the element + * byteoff bytes away in a tile vertical slice is always this + * many bytes away in the ZA storage, regardless of the + * size of the tile element, assuming that byteoff is a multiple + * of the element size. Again this is because of the interleaving + * of the tiles. For instance if we have 1 byte per element then + * each row of the ZA storage has one byte of the vslice data, + * and (counting from 0) byte 8 goes in row 8 of the storage + * at offset (8 * row-size-in-bytes). + * If we have 8 bytes per element then each row of the ZA storage + * has 8 bytes of the data, but there are 8 interleaved tiles and + * so byte 8 of the data goes into row 1 of the tile, + * which is again row 8 of the storage, so the offset is still + * (8 * row-size-in-bytes). Similarly for other element sizes. + */ +#define tile_vslice_offset(byteoff) ((byteoff) * sizeof(ARMVectorReg)) + + +/* + * Move Zreg vector to ZArray column. + */ +#define DO_MOVA_C(NAME, TYPE, H) \ +void HELPER(NAME)(void *za, void *vn, void *vg, uint32_t desc) \ +{ \ + int i, oprsz = simd_oprsz(desc); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + *(TYPE *)(za + tile_vslice_offset(i)) = *(TYPE *)(vn + H(i)); \ + } \ + i += sizeof(TYPE); \ + pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +DO_MOVA_C(sme_mova_cz_b, uint8_t, H1) +DO_MOVA_C(sme_mova_cz_h, uint16_t, H1_2) +DO_MOVA_C(sme_mova_cz_s, uint32_t, H1_4) + +void HELPER(sme_mova_cz_d)(void *za, void *vn, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 8; + uint8_t *pg = vg; + uint64_t *n = vn; + uint64_t *a = za; + + for (i = 0; i < oprsz; i++) { + if (pg[H1(i)] & 1) { + a[tile_vslice_index(i)] = n[i]; + } + } +} + +void HELPER(sme_mova_cz_q)(void *za, void *vn, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 16; + uint16_t *pg = vg; + Int128 *n = vn; + Int128 *a = za; + + /* + * Int128 is used here simply to copy 16 bytes, and to simplify + * the address arithmetic. + */ + for (i = 0; i < oprsz; i++) { + if (pg[H2(i)] & 1) { + a[tile_vslice_index(i)] = n[i]; + } + } +} + +#undef DO_MOVA_C + +/* + * Move ZArray column to Zreg vector. + */ +#define DO_MOVA_Z(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *za, void *vg, uint32_t desc) \ +{ \ + int i, oprsz = simd_oprsz(desc); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + *(TYPE *)(vd + H(i)) = *(TYPE *)(za + tile_vslice_offset(i)); \ + } \ + i += sizeof(TYPE); \ + pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +DO_MOVA_Z(sme_mova_zc_b, uint8_t, H1) +DO_MOVA_Z(sme_mova_zc_h, uint16_t, H1_2) +DO_MOVA_Z(sme_mova_zc_s, uint32_t, H1_4) + +void HELPER(sme_mova_zc_d)(void *vd, void *za, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 8; + uint8_t *pg = vg; + uint64_t *d = vd; + uint64_t *a = za; + + for (i = 0; i < oprsz; i++) { + if (pg[H1(i)] & 1) { + d[i] = a[tile_vslice_index(i)]; + } + } +} + +void HELPER(sme_mova_zc_q)(void *vd, void *za, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 16; + uint16_t *pg = vg; + Int128 *d = vd; + Int128 *a = za; + + /* + * Int128 is used here simply to copy 16 bytes, and to simplify + * the address arithmetic. + */ + for (i = 0; i < oprsz; i++, za += sizeof(ARMVectorReg)) { + if (pg[H2(i)] & 1) { + d[i] = a[tile_vslice_index(i)]; + } + } +} + +#undef DO_MOVA_Z + +/* + * Clear elements in a tile slice comprising len bytes. + */ + +typedef void ClearFn(void *ptr, size_t off, size_t len); + +static void clear_horizontal(void *ptr, size_t off, size_t len) +{ + memset(ptr + off, 0, len); +} + +static void clear_vertical_b(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; ++i) { + *(uint8_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_h(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 2) { + *(uint16_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_s(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 4) { + *(uint32_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_d(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 8) { + *(uint64_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_q(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 16) { + memset(vptr + tile_vslice_offset(i + off), 0, 16); + } +} + +/* + * Copy elements from an array into a tile slice comprising len bytes. + */ + +typedef void CopyFn(void *dst, const void *src, size_t len); + +static void copy_horizontal(void *dst, const void *src, size_t len) +{ + memcpy(dst, src, len); +} + +static void copy_vertical_b(void *vdst, const void *vsrc, size_t len) +{ + const uint8_t *src = vsrc; + uint8_t *dst = vdst; + size_t i; + + for (i = 0; i < len; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_h(void *vdst, const void *vsrc, size_t len) +{ + const uint16_t *src = vsrc; + uint16_t *dst = vdst; + size_t i; + + for (i = 0; i < len / 2; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_s(void *vdst, const void *vsrc, size_t len) +{ + const uint32_t *src = vsrc; + uint32_t *dst = vdst; + size_t i; + + for (i = 0; i < len / 4; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_d(void *vdst, const void *vsrc, size_t len) +{ + const uint64_t *src = vsrc; + uint64_t *dst = vdst; + size_t i; + + for (i = 0; i < len / 8; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_q(void *vdst, const void *vsrc, size_t len) +{ + for (size_t i = 0; i < len; i += 16) { + memcpy(vdst + tile_vslice_offset(i), vsrc + i, 16); + } +} + +/* + * Host and TLB primitives for vertical tile slice addressing. + */ + +#define DO_LD(NAME, TYPE, HOST, TLB) \ +static inline void sme_##NAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + TYPE val = HOST(host); \ + *(TYPE *)(za + tile_vslice_offset(off)) = val; \ +} \ +static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ + intptr_t off, target_ulong addr, uintptr_t ra) \ +{ \ + TYPE val = TLB(env, useronly_clean_ptr(addr), ra); \ + *(TYPE *)(za + tile_vslice_offset(off)) = val; \ +} + +#define DO_ST(NAME, TYPE, HOST, TLB) \ +static inline void sme_##NAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + TYPE val = *(TYPE *)(za + tile_vslice_offset(off)); \ + HOST(host, val); \ +} \ +static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ + intptr_t off, target_ulong addr, uintptr_t ra) \ +{ \ + TYPE val = *(TYPE *)(za + tile_vslice_offset(off)); \ + TLB(env, useronly_clean_ptr(addr), val, ra); \ +} + +/* + * The ARMVectorReg elements are stored in host-endian 64-bit units. + * For 128-bit quantities, the sequence defined by the Elem[] pseudocode + * corresponds to storing the two 64-bit pieces in little-endian order. + */ +#define DO_LDQ(HNAME, VNAME, BE, HOST, TLB) \ +static inline void HNAME##_host(void *za, intptr_t off, void *host) \ +{ \ + uint64_t val0 = HOST(host), val1 = HOST(host + 8); \ + uint64_t *ptr = za + off; \ + ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ +} \ +static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + HNAME##_host(za, tile_vslice_offset(off), host); \ +} \ +static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + uint64_t val0 = TLB(env, useronly_clean_ptr(addr), ra); \ + uint64_t val1 = TLB(env, useronly_clean_ptr(addr + 8), ra); \ + uint64_t *ptr = za + off; \ + ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ +} \ +static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ +} + +#define DO_STQ(HNAME, VNAME, BE, HOST, TLB) \ +static inline void HNAME##_host(void *za, intptr_t off, void *host) \ +{ \ + uint64_t *ptr = za + off; \ + HOST(host, ptr[BE]); \ + HOST(host + 1, ptr[!BE]); \ +} \ +static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + HNAME##_host(za, tile_vslice_offset(off), host); \ +} \ +static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + uint64_t *ptr = za + off; \ + TLB(env, useronly_clean_ptr(addr), ptr[BE], ra); \ + TLB(env, useronly_clean_ptr(addr + 8), ptr[!BE], ra); \ +} \ +static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ +} + +DO_LD(ld1b, uint8_t, ldub_p, cpu_ldub_data_ra) +DO_LD(ld1h_be, uint16_t, lduw_be_p, cpu_lduw_be_data_ra) +DO_LD(ld1h_le, uint16_t, lduw_le_p, cpu_lduw_le_data_ra) +DO_LD(ld1s_be, uint32_t, ldl_be_p, cpu_ldl_be_data_ra) +DO_LD(ld1s_le, uint32_t, ldl_le_p, cpu_ldl_le_data_ra) +DO_LD(ld1d_be, uint64_t, ldq_be_p, cpu_ldq_be_data_ra) +DO_LD(ld1d_le, uint64_t, ldq_le_p, cpu_ldq_le_data_ra) + +DO_LDQ(sve_ld1qq_be, sme_ld1q_be, 1, ldq_be_p, cpu_ldq_be_data_ra) +DO_LDQ(sve_ld1qq_le, sme_ld1q_le, 0, ldq_le_p, cpu_ldq_le_data_ra) + +DO_ST(st1b, uint8_t, stb_p, cpu_stb_data_ra) +DO_ST(st1h_be, uint16_t, stw_be_p, cpu_stw_be_data_ra) +DO_ST(st1h_le, uint16_t, stw_le_p, cpu_stw_le_data_ra) +DO_ST(st1s_be, uint32_t, stl_be_p, cpu_stl_be_data_ra) +DO_ST(st1s_le, uint32_t, stl_le_p, cpu_stl_le_data_ra) +DO_ST(st1d_be, uint64_t, stq_be_p, cpu_stq_be_data_ra) +DO_ST(st1d_le, uint64_t, stq_le_p, cpu_stq_le_data_ra) + +DO_STQ(sve_st1qq_be, sme_st1q_be, 1, stq_be_p, cpu_stq_be_data_ra) +DO_STQ(sve_st1qq_le, sme_st1q_le, 0, stq_le_p, cpu_stq_le_data_ra) + +#undef DO_LD +#undef DO_ST +#undef DO_LDQ +#undef DO_STQ + +/* + * Common helper for all contiguous predicated loads. + */ + +static inline QEMU_ALWAYS_INLINE +void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, + const target_ulong addr, uint32_t desc, const uintptr_t ra, + const int esz, uint32_t mtedesc, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn, + ClearFn *clr_fn, + CopyFn *cpy_fn) +{ + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t esize = 1 << esz; + intptr_t reg_off, reg_last; + SVEContLdSt info; + void *host; + int flags; + + /* Find the active elements. */ + if (!sve_cont_ldst_elements(&info, addr, vg, reg_max, esz, esize)) { + /* The entire predicate was false; no load occurs. */ + clr_fn(za, 0, reg_max); + return; + } + + /* Probe the page(s). Exit with exception for any invalid page. */ + sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_LOAD, ra); + + /* Handle watchpoints for all active elements. */ + sve_cont_ldst_watchpoints(&info, env, vg, addr, esize, esize, + BP_MEM_READ, ra); + + /* + * Handle mte checks for all active elements. + * Since TBI must be set for MTE, !mtedesc => !mte_active. + */ + if (mtedesc) { + sve_cont_ldst_mte_check(&info, env, vg, addr, esize, esize, + mtedesc, ra); + } + + flags = info.page[0].flags | info.page[1].flags; + if (unlikely(flags != 0)) { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + /* + * At least one page includes MMIO. + * Any bus operation can fail with cpu_transaction_failed, + * which for ARM will raise SyncExternal. Perform the load + * into scratch memory to preserve register state until the end. + */ + ARMVectorReg scratch = { }; + + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[1]; + if (reg_last < 0) { + reg_last = info.reg_off_split; + if (reg_last < 0) { + reg_last = info.reg_off_last[0]; + } + } + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + tlb_fn(env, &scratch, reg_off, addr + reg_off, ra); + } + reg_off += esize; + } while (reg_off & 63); + } while (reg_off <= reg_last); + + cpy_fn(za, &scratch, reg_max); + return; +#endif + } + + /* The entire operation is in RAM, on valid pages. */ + + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[0]; + host = info.page[0].host; + + if (!vertical) { + memset(za, 0, reg_max); + } else if (reg_off) { + clr_fn(za, 0, reg_off); + } + + while (reg_off <= reg_last) { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } else if (vertical) { + clr_fn(za, reg_off, esize); + } + reg_off += esize; + } while (reg_off <= reg_last && (reg_off & 63)); + } + + /* + * Use the slow path to manage the cross-page misalignment. + * But we know this is RAM and cannot trap. + */ + reg_off = info.reg_off_split; + if (unlikely(reg_off >= 0)) { + tlb_fn(env, za, reg_off, addr + reg_off, ra); + } + + reg_off = info.reg_off_first[1]; + if (unlikely(reg_off >= 0)) { + reg_last = info.reg_off_last[1]; + host = info.page[1].host; + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } else if (vertical) { + clr_fn(za, reg_off, esize); + } + reg_off += esize; + } while (reg_off & 63); + } while (reg_off <= reg_last); + } +} + +static inline QEMU_ALWAYS_INLINE +void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, + target_ulong addr, uint32_t desc, uintptr_t ra, + const int esz, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn, + ClearFn *clr_fn, + CopyFn *cpy_fn) +{ + uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + int bit55 = extract64(addr, 55, 1); + + /* Remove mtedesc from the normal sve descriptor. */ + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ + if (!tbi_check(desc, bit55) || + tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + + sme_ld1(env, za, vg, addr, desc, ra, esz, mtedesc, vertical, + host_fn, tlb_fn, clr_fn, cpy_fn); +} + +#define DO_LD(L, END, ESZ) \ +void HELPER(sme_ld1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ + sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ + clear_horizontal, copy_horizontal); \ +} \ +void HELPER(sme_ld1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ + sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ + clear_vertical_##L, copy_vertical_##L); \ +} \ +void HELPER(sme_ld1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ + sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ + clear_horizontal, copy_horizontal); \ +} \ +void HELPER(sme_ld1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ + sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ + clear_vertical_##L, copy_vertical_##L); \ +} + +DO_LD(b, , MO_8) +DO_LD(h, _be, MO_16) +DO_LD(h, _le, MO_16) +DO_LD(s, _be, MO_32) +DO_LD(s, _le, MO_32) +DO_LD(d, _be, MO_64) +DO_LD(d, _le, MO_64) +DO_LD(q, _be, MO_128) +DO_LD(q, _le, MO_128) + +#undef DO_LD + +/* + * Common helper for all contiguous predicated stores. + */ + +static inline QEMU_ALWAYS_INLINE +void sme_st1(CPUARMState *env, void *za, uint64_t *vg, + const target_ulong addr, uint32_t desc, const uintptr_t ra, + const int esz, uint32_t mtedesc, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn) +{ + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t esize = 1 << esz; + intptr_t reg_off, reg_last; + SVEContLdSt info; + void *host; + int flags; + + /* Find the active elements. */ + if (!sve_cont_ldst_elements(&info, addr, vg, reg_max, esz, esize)) { + /* The entire predicate was false; no store occurs. */ + return; + } + + /* Probe the page(s). Exit with exception for any invalid page. */ + sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_STORE, ra); + + /* Handle watchpoints for all active elements. */ + sve_cont_ldst_watchpoints(&info, env, vg, addr, esize, esize, + BP_MEM_WRITE, ra); + + /* + * Handle mte checks for all active elements. + * Since TBI must be set for MTE, !mtedesc => !mte_active. + */ + if (mtedesc) { + sve_cont_ldst_mte_check(&info, env, vg, addr, esize, esize, + mtedesc, ra); + } + + flags = info.page[0].flags | info.page[1].flags; + if (unlikely(flags != 0)) { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + /* + * At least one page includes MMIO. + * Any bus operation can fail with cpu_transaction_failed, + * which for ARM will raise SyncExternal. We cannot avoid + * this fault and will leave with the store incomplete. + */ + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[1]; + if (reg_last < 0) { + reg_last = info.reg_off_split; + if (reg_last < 0) { + reg_last = info.reg_off_last[0]; + } + } + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + tlb_fn(env, za, reg_off, addr + reg_off, ra); + } + reg_off += esize; + } while (reg_off & 63); + } while (reg_off <= reg_last); + return; +#endif + } + + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[0]; + host = info.page[0].host; + + while (reg_off <= reg_last) { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } + reg_off += 1 << esz; + } while (reg_off <= reg_last && (reg_off & 63)); + } + + /* + * Use the slow path to manage the cross-page misalignment. + * But we know this is RAM and cannot trap. + */ + reg_off = info.reg_off_split; + if (unlikely(reg_off >= 0)) { + tlb_fn(env, za, reg_off, addr + reg_off, ra); + } + + reg_off = info.reg_off_first[1]; + if (unlikely(reg_off >= 0)) { + reg_last = info.reg_off_last[1]; + host = info.page[1].host; + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } + reg_off += 1 << esz; + } while (reg_off & 63); + } while (reg_off <= reg_last); + } +} + +static inline QEMU_ALWAYS_INLINE +void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, + uint32_t desc, uintptr_t ra, int esz, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn) +{ + uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + int bit55 = extract64(addr, 55, 1); + + /* Remove mtedesc from the normal sve descriptor. */ + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ + if (!tbi_check(desc, bit55) || + tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + + sme_st1(env, za, vg, addr, desc, ra, esz, mtedesc, + vertical, host_fn, tlb_fn); +} + +#define DO_ST(L, END, ESZ) \ +void HELPER(sme_st1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ + sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ +} \ +void HELPER(sme_st1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ + sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ +} \ +void HELPER(sme_st1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ + sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ +} \ +void HELPER(sme_st1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ + sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ +} + +DO_ST(b, , MO_8) +DO_ST(h, _be, MO_16) +DO_ST(h, _le, MO_16) +DO_ST(s, _be, MO_32) +DO_ST(s, _le, MO_32) +DO_ST(d, _be, MO_64) +DO_ST(d, _le, MO_64) +DO_ST(q, _be, MO_128) +DO_ST(q, _le, MO_128) + +#undef DO_ST + +void HELPER(sme_addha_s)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 4; + uint64_t *pn = vpn, *pm = vpm; + uint32_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ) { + uint64_t pa = pn[row >> 4]; + do { + if (pa & 1) { + for (col = 0; col < oprsz; ) { + uint64_t pb = pm[col >> 4]; + do { + if (pb & 1) { + zda[tile_vslice_index(row) + H4(col)] += zn[H4(col)]; + } + pb >>= 4; + } while (++col & 15); + } + } + pa >>= 4; + } while (++row & 15); + } +} + +void HELPER(sme_addha_d)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + uint8_t *pn = vpn, *pm = vpm; + uint64_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ++row) { + if (pn[H1(row)] & 1) { + for (col = 0; col < oprsz; ++col) { + if (pm[H1(col)] & 1) { + zda[tile_vslice_index(row) + col] += zn[col]; + } + } + } + } +} + +void HELPER(sme_addva_s)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 4; + uint64_t *pn = vpn, *pm = vpm; + uint32_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ) { + uint64_t pa = pn[row >> 4]; + do { + if (pa & 1) { + uint32_t zn_row = zn[H4(row)]; + for (col = 0; col < oprsz; ) { + uint64_t pb = pm[col >> 4]; + do { + if (pb & 1) { + zda[tile_vslice_index(row) + H4(col)] += zn_row; + } + pb >>= 4; + } while (++col & 15); + } + } + pa >>= 4; + } while (++row & 15); + } +} + +void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + uint8_t *pn = vpn, *pm = vpm; + uint64_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ++row) { + if (pn[H1(row)] & 1) { + uint64_t zn_row = zn[row]; + for (col = 0; col < oprsz; ++col) { + if (pm[H1(col)] & 1) { + zda[tile_vslice_index(row) + col] += zn_row; + } + } + } + } +} + +void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, void *vst, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + uint32_t neg = simd_data(desc) << 31; + uint16_t *pn = vpn, *pm = vpm; + float_status fpst; + + /* + * Make a copy of float_status because this operation does not + * update the cumulative fp exception status. It also produces + * default nans. + */ + fpst = *(float_status *)vst; + set_default_nan_mode(true, &fpst); + + for (row = 0; row < oprsz; ) { + uint16_t pa = pn[H2(row >> 4)]; + do { + if (pa & 1) { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)) ^ neg; + + for (col = 0; col < oprsz; ) { + uint16_t pb = pm[H2(col >> 4)]; + do { + if (pb & 1) { + uint32_t *a = vza_row + H1_4(col); + uint32_t *m = vzm + H1_4(col); + *a = float32_muladd(n, *m, *a, 0, vst); + } + col += 4; + pb >>= 4; + } while (col & 15); + } + } + row += 4; + pa >>= 4; + } while (row & 15); + } +} + +void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, void *vst, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + uint64_t neg = (uint64_t)simd_data(desc) << 63; + uint64_t *za = vza, *zn = vzn, *zm = vzm; + uint8_t *pn = vpn, *pm = vpm; + float_status fpst = *(float_status *)vst; + + set_default_nan_mode(true, &fpst); + + for (row = 0; row < oprsz; ++row) { + if (pn[H1(row)] & 1) { + uint64_t *za_row = &za[tile_vslice_index(row)]; + uint64_t n = zn[row] ^ neg; + + for (col = 0; col < oprsz; ++col) { + if (pm[H1(col)] & 1) { + uint64_t *a = &za_row[col]; + *a = float64_muladd(n, zm[col], *a, 0, &fpst); + } + } + } + } +} + +/* + * Alter PAIR as needed for controlling predicates being false, + * and for NEG on an enabled row element. + */ +static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg) +{ + /* + * The pseudocode uses a conditional negate after the conditional zero. + * It is simpler here to unconditionally negate before conditional zero. + */ + pair ^= neg; + if (!(pg & 1)) { + pair &= 0xffff0000u; + } + if (!(pg & 4)) { + pair &= 0x0000ffffu; + } + return pair; +} + +static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, + float_status *s_std, float_status *s_odd) +{ + float64 e1r = float16_to_float64(e1 & 0xffff, true, s_std); + float64 e1c = float16_to_float64(e1 >> 16, true, s_std); + float64 e2r = float16_to_float64(e2 & 0xffff, true, s_std); + float64 e2c = float16_to_float64(e2 >> 16, true, s_std); + float64 t64; + float32 t32; + + /* + * The ARM pseudocode function FPDot performs both multiplies + * and the add with a single rounding operation. Emulate this + * by performing the first multiply in round-to-odd, then doing + * the second multiply as fused multiply-add, and rounding to + * float32 all in one step. + */ + t64 = float64_mul(e1r, e2r, s_odd); + t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std); + + /* This conversion is exact, because we've already rounded. */ + t32 = float64_to_float32(t64, s_std); + + /* The final accumulation step is not fused. */ + return float32_add(sum, t32, s_std); +} + +void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, void *vst, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + uint32_t neg = simd_data(desc) * 0x80008000u; + uint16_t *pn = vpn, *pm = vpm; + float_status fpst_odd, fpst_std; + + /* + * Make a copy of float_status because this operation does not + * update the cumulative fp exception status. It also produces + * default nans. Make a second copy with round-to-odd -- see above. + */ + fpst_std = *(float_status *)vst; + set_default_nan_mode(true, &fpst_std); + fpst_odd = fpst_std; + set_float_rounding_mode(float_round_to_odd, &fpst_odd); + + for (row = 0; row < oprsz; ) { + uint16_t prow = pn[H2(row >> 4)]; + do { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)); + + n = f16mop_adj_pair(n, prow, neg); + + for (col = 0; col < oprsz; ) { + uint16_t pcol = pm[H2(col >> 4)]; + do { + if (prow & pcol & 0b0101) { + uint32_t *a = vza_row + H1_4(col); + uint32_t m = *(uint32_t *)(vzm + H1_4(col)); + + m = f16mop_adj_pair(m, pcol, 0); + *a = f16_dotadd(*a, n, m, &fpst_std, &fpst_odd); + + col += 4; + pcol >>= 4; + } + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } +} + +void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + uint32_t neg = simd_data(desc) * 0x80008000u; + uint16_t *pn = vpn, *pm = vpm; + + for (row = 0; row < oprsz; ) { + uint16_t prow = pn[H2(row >> 4)]; + do { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)); + + n = f16mop_adj_pair(n, prow, neg); + + for (col = 0; col < oprsz; ) { + uint16_t pcol = pm[H2(col >> 4)]; + do { + if (prow & pcol & 0b0101) { + uint32_t *a = vza_row + H1_4(col); + uint32_t m = *(uint32_t *)(vzm + H1_4(col)); + + m = f16mop_adj_pair(m, pcol, 0); + *a = bfdotadd(*a, n, m); + + col += 4; + pcol >>= 4; + } + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } +} + +typedef uint64_t IMOPFn(uint64_t, uint64_t, uint64_t, uint8_t, bool); + +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) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + bool neg = simd_data(desc); + + for (row = 0; row < oprsz; ++row) { + uint8_t pa = pn[H1(row)]; + uint64_t *za_row = &za[tile_vslice_index(row)]; + uint64_t n = zn[row]; + + for (col = 0; col < oprsz; ++col) { + uint8_t pb = pm[H1(col)]; + uint64_t *a = &za_row[col]; + + *a = fn(n, zm[col], *a, pa & pb, neg); + } + } +} + +#define DEF_IMOP_32(NAME, NTYPE, MTYPE) \ +static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ +{ \ + uint32_t sum0 = 0, sum1 = 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; \ +} + +#define DEF_IMOP_64(NAME, NTYPE, MTYPE) \ +static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ +{ \ + uint64_t sum = 0; \ + /* Apply P to N as a mask, making the inactive elements 0. */ \ + n &= expand_pred_h(p); \ + sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ + sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ + sum += (NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ + sum += (NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ + return neg ? a - sum : a + sum; \ +} + +DEF_IMOP_32(smopa_s, int8_t, int8_t) +DEF_IMOP_32(umopa_s, uint8_t, uint8_t) +DEF_IMOP_32(sumopa_s, int8_t, uint8_t) +DEF_IMOP_32(usmopa_s, uint8_t, int8_t) + +DEF_IMOP_64(smopa_d, int16_t, int16_t) +DEF_IMOP_64(umopa_d, uint16_t, uint16_t) +DEF_IMOP_64(sumopa_d, int16_t, uint16_t) +DEF_IMOP_64(usmopa_d, uint16_t, int16_t) + +#define DEF_IMOPH(NAME) \ + 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) diff --git a/target/arm/sve.decode b/target/arm/sve.decode index a54feb2f61d..14b3a69c364 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -449,14 +449,17 @@ INDEX_ri 00000100 esz:2 1 imm:s5 010001 rn:5 rd:5 # SVE index generation (register start, register increment) INDEX_rr 00000100 .. 1 ..... 010011 ..... ..... @rd_rn_rm -### SVE Stack Allocation Group +### SVE / Streaming SVE Stack Allocation Group # SVE stack frame adjustment ADDVL 00000100 001 ..... 01010 ...... ..... @rd_rn_i6 +ADDSVL 00000100 001 ..... 01011 ...... ..... @rd_rn_i6 ADDPL 00000100 011 ..... 01010 ...... ..... @rd_rn_i6 +ADDSPL 00000100 011 ..... 01011 ...... ..... @rd_rn_i6 # SVE stack frame size RDVL 00000100 101 11111 01010 imm:s6 rd:5 +RDSVL 00000100 101 11111 01011 imm:s6 rd:5 ### SVE Bitwise Shift - Unpredicated Group @@ -649,6 +652,7 @@ REVB 00000101 .. 1001 00 100 ... ..... ..... @rd_pg_rn REVH 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn REVW 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn +REVD 00000101 00 1011 10 100 ... ..... ..... @rd_pg_rn_e0 # SVE vector splice (predicated, destructive) SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm @@ -1183,10 +1187,10 @@ LD1RO_zpri 1010010 .. 01 0.... 001 ... ..... ..... \ @rpri_load_msz nreg=0 # SVE 32-bit gather prefetch (scalar plus 32-bit scaled offsets) -PRF 1000010 00 -1 ----- 0-- --- ----- 0 ---- +PRF_ns 1000010 00 -1 ----- 0-- --- ----- 0 ---- # SVE 32-bit gather prefetch (vector plus immediate) -PRF 1000010 -- 00 ----- 111 --- ----- 0 ---- +PRF_ns 1000010 -- 00 ----- 111 --- ----- 0 ---- # SVE contiguous prefetch (scalar plus immediate) PRF 1000010 11 1- ----- 0-- --- ----- 0 ---- @@ -1223,13 +1227,13 @@ LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ @rpri_g_load esz=3 # SVE 64-bit gather prefetch (scalar plus 64-bit scaled offsets) -PRF 1100010 00 11 ----- 1-- --- ----- 0 ---- +PRF_ns 1100010 00 11 ----- 1-- --- ----- 0 ---- # SVE 64-bit gather prefetch (scalar plus unpacked 32-bit scaled offsets) -PRF 1100010 00 -1 ----- 0-- --- ----- 0 ---- +PRF_ns 1100010 00 -1 ----- 0-- --- ----- 0 ---- # SVE 64-bit gather prefetch (vector plus immediate) -PRF 1100010 -- 00 ----- 111 --- ----- 0 ---- +PRF_ns 1100010 -- 00 ----- 111 --- ----- 0 ---- ### SVE Memory Store Group @@ -1671,3 +1675,28 @@ BFMLALT_zzxw 01100100 11 1 ..... 0100.1 ..... ..... @rrxr_3a esz=2 ### SVE2 floating-point bfloat16 dot-product (indexed) BFDOT_zzxz 01100100 01 1 ..... 010000 ..... ..... @rrxr_2 esz=2 + +### SVE broadcast predicate element + +&psel esz pd pn pm rv imm +%psel_rv 16:2 !function=plus_12 +%psel_imm_b 22:2 19:2 +%psel_imm_h 22:2 20:1 +%psel_imm_s 22:2 +%psel_imm_d 23:1 +@psel ........ .. . ... .. .. pn:4 . pm:4 . pd:4 \ + &psel rv=%psel_rv + +PSEL 00100101 .. 1 ..1 .. 01 .... 0 .... 0 .... \ + @psel esz=0 imm=%psel_imm_b +PSEL 00100101 .. 1 .10 .. 01 .... 0 .... 0 .... \ + @psel esz=1 imm=%psel_imm_h +PSEL 00100101 .. 1 100 .. 01 .... 0 .... 0 .... \ + @psel esz=2 imm=%psel_imm_s +PSEL 00100101 .1 1 000 .. 01 .... 0 .... 0 .... \ + @psel esz=3 imm=%psel_imm_d + +### SVE clamp + +SCLAMP 01000100 .. 0 ..... 110000 ..... ..... @rda_rn_rm +UCLAMP 01000100 .. 0 ..... 110001 ..... ..... @rda_rn_rm diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 1654c0bbf9e..d6f7ef94fe6 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -931,6 +931,22 @@ DO_ZPZ_D(sve_revh_d, uint64_t, hswap64) DO_ZPZ_D(sve_revw_d, uint64_t, wswap64) +void HELPER(sme_revd_q)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 2) { + if (pg[H1(i)] & 1) { + uint64_t n0 = n[i + 0]; + uint64_t n1 = n[i + 1]; + d[i + 0] = n1; + d[i + 1] = n0; + } + } +} + DO_ZPZ(sve_rbit_b, uint8_t, H1, revbit8) DO_ZPZ(sve_rbit_h, uint16_t, H1_2, revbit16) DO_ZPZ(sve_rbit_s, uint32_t, H1_4, revbit32) @@ -3565,6 +3581,18 @@ void HELPER(sve_sel_zpzz_d)(void *vd, void *vn, void *vm, } } +void HELPER(sve_sel_zpzz_q)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 16; + Int128 *d = vd, *n = vn, *m = vm; + uint16_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + d[i] = (pg[H2(i)] & 1 ? n : m)[i]; + } +} + /* Two operand comparison controlled by a predicate. * ??? It is very tempting to want to be able to expand this inline * with x86 instructions, e.g. @@ -5337,6 +5365,9 @@ bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env, #ifdef CONFIG_USER_ONLY memset(&info->attrs, 0, sizeof(info->attrs)); + /* Require both MAP_ANON and PROT_MTE -- see allocation_tag_mem. */ + arm_tlb_mte_tagged(&info->attrs) = + (flags & PAGE_ANON) && (flags & PAGE_MTE); #else /* * Find the iotlbentry for addr and return the transaction attributes. @@ -5986,7 +6017,7 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, * Disable MTE checking if the Tagged bit is not set. Since TBI must * be set within MTEDESC for MTE, !mtedesc => !mte_active. */ - if (arm_tlb_mte_tagged(&info.page[0].attrs)) { + if (!arm_tlb_mte_tagged(&info.page[0].attrs)) { mtedesc = 0; } diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index c105f9e6ba5..73df5e37938 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -48,6 +48,7 @@ enum arm_exception_class { EC_AA64_SMC = 0x17, EC_SYSTEMREGISTERTRAP = 0x18, EC_SVEACCESSTRAP = 0x19, + EC_SMETRAP = 0x1d, EC_INSNABORT = 0x20, EC_INSNABORT_SAME_EL = 0x21, EC_PCALIGNMENT = 0x22, @@ -68,6 +69,13 @@ enum arm_exception_class { EC_AA64_BKPT = 0x3c, }; +typedef enum { + SME_ET_AccessTrap, + SME_ET_Streaming, + SME_ET_NotStreaming, + SME_ET_InactiveZA, +} SMEExceptionType; + #define ARM_EL_EC_SHIFT 26 #define ARM_EL_IL_SHIFT 25 #define ARM_EL_ISV_SHIFT 24 @@ -207,6 +215,12 @@ static inline uint32_t syn_sve_access_trap(void) return EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT; } +static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) +{ + return (EC_SMETRAP << ARM_EL_EC_SHIFT) + | (is_16bit ? 0 : ARM_EL_IL) | etype; +} + static inline uint32_t syn_pactrap(void) { return EC_PACTRAP << ARM_EL_EC_SHIFT; diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c index 7d8a86b3c45..5a709eab56f 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tlb_helper.c @@ -20,7 +20,7 @@ bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) return true; } if (arm_feature(env, ARM_FEATURE_LPAE) - && (regime_tcr(env, mmu_idx)->raw_tcr & TTBCR_EAE)) { + && (regime_tcr(env, mmu_idx) & TTBCR_EAE)) { return true; } return false; diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index c5b0c7808c7..29d4077c793 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -1155,7 +1155,7 @@ static void do_vec_ld(DisasContext *s, int destidx, int element, * unallocated-encoding checks (otherwise the syndrome information * for the resulting exception will be incorrect). */ -static bool fp_access_check(DisasContext *s) +static bool fp_access_check_only(DisasContext *s) { if (s->fp_excp_el) { assert(!s->fp_access_checked); @@ -1170,21 +1170,94 @@ static bool fp_access_check(DisasContext *s) return true; } -/* Check that SVE access is enabled. If it is, return true. +static bool fp_access_check(DisasContext *s) +{ + if (!fp_access_check_only(s)) { + return false; + } + if (s->sme_trap_nonstreaming && s->is_nonstreaming) { + gen_exception_insn(s, s->pc_curr, EXCP_UDEF, + syn_smetrap(SME_ET_Streaming, false)); + return false; + } + return true; +} + +/* + * Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. + * This function corresponds to CheckSVEEnabled(). */ bool sve_access_check(DisasContext *s) { - if (s->sve_excp_el) { - assert(!s->sve_access_checked); - s->sve_access_checked = true; - + if (s->pstate_sm || !dc_isar_feature(aa64_sve, s)) { + assert(dc_isar_feature(aa64_sme, s)); + if (!sme_sm_enabled_check(s)) { + goto fail_exit; + } + } else if (s->sve_excp_el) { gen_exception_insn_el(s, s->pc_curr, EXCP_UDEF, syn_sve_access_trap(), s->sve_excp_el); - return false; + goto fail_exit; } s->sve_access_checked = true; return fp_access_check(s); + + fail_exit: + /* Assert that we only raise one exception per instruction. */ + assert(!s->sve_access_checked); + s->sve_access_checked = true; + return false; +} + +/* + * Check that SME access is enabled, raise an exception if not. + * Note that this function corresponds to CheckSMEAccess and is + * only used directly for cpregs. + */ +static bool sme_access_check(DisasContext *s) +{ + if (s->sme_excp_el) { + gen_exception_insn_el(s, s->pc_curr, EXCP_UDEF, + syn_smetrap(SME_ET_AccessTrap, false), + s->sme_excp_el); + return false; + } + return true; +} + +/* This function corresponds to CheckSMEEnabled. */ +bool sme_enabled_check(DisasContext *s) +{ + /* + * Note that unlike sve_excp_el, we have not constrained sme_excp_el + * to be zero when fp_excp_el has priority. This is because we need + * sme_excp_el by itself for cpregs access checks. + */ + if (!s->fp_excp_el || s->sme_excp_el < s->fp_excp_el) { + s->fp_access_checked = true; + return sme_access_check(s); + } + return fp_access_check_only(s); +} + +/* Common subroutine for CheckSMEAnd*Enabled. */ +bool sme_enabled_check_with_svcr(DisasContext *s, unsigned req) +{ + if (!sme_enabled_check(s)) { + return false; + } + if (FIELD_EX64(req, SVCR, SM) && !s->pstate_sm) { + gen_exception_insn(s, s->pc_curr, EXCP_UDEF, + syn_smetrap(SME_ET_NotStreaming, false)); + return false; + } + if (FIELD_EX64(req, SVCR, ZA) && !s->pstate_za) { + gen_exception_insn(s, s->pc_curr, EXCP_UDEF, + syn_smetrap(SME_ET_InactiveZA, false)); + return false; + } + return true; } /* @@ -1746,6 +1819,30 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, } break; + case 0x1b: /* SVCR* */ + if (!dc_isar_feature(aa64_sme, s) || crm < 2 || crm > 7) { + goto do_unallocated; + } + if (sme_access_check(s)) { + bool i = crm & 1; + bool changed = false; + + if ((crm & 2) && i != s->pstate_sm) { + gen_helper_set_pstate_sm(cpu_env, tcg_constant_i32(i)); + changed = true; + } + if ((crm & 4) && i != s->pstate_za) { + gen_helper_set_pstate_za(cpu_env, tcg_constant_i32(i)); + changed = true; + } + if (changed) { + gen_rebuild_hflags(s); + } else { + s->base.is_jmp = DISAS_NEXT; + } + } + break; + default: do_unallocated: unallocated_encoding(s); @@ -1954,10 +2051,12 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, default: g_assert_not_reached(); } - if ((ri->type & ARM_CP_FPU) && !fp_access_check(s)) { + if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) { return; } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { return; + } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) { + return; } if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { @@ -3039,7 +3138,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, bool is_store = false; bool is_extended = false; bool is_unpriv = (idx == 2); - bool iss_valid = !is_vector; + bool iss_valid; bool post_index; bool writeback; int memidx; @@ -3092,6 +3191,8 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, g_assert_not_reached(); } + iss_valid = !is_vector && !writeback; + if (rn == 31) { gen_check_sp_alignment(s); } @@ -14524,6 +14625,23 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) } } +/* + * Include the generated SME FA64 decoder. + */ + +#include "decode-sme-fa64.c.inc" + +static bool trans_OK(DisasContext *s, arg_OK *a) +{ + return true; +} + +static bool trans_FAIL(DisasContext *s, arg_OK *a) +{ + s->is_nonstreaming = true; + return true; +} + /** * is_guarded_page: * @env: The cpu environment @@ -14639,7 +14757,9 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); + dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL); dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; + dc->svl = (EX_TBFLAG_A64(tb_flags, SVL) + 1) * 16; dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE); dc->bt = EX_TBFLAG_A64(tb_flags, BT); dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); @@ -14647,6 +14767,9 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->ata = EX_TBFLAG_A64(tb_flags, ATA); dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE); dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE); + dc->pstate_sm = EX_TBFLAG_A64(tb_flags, PSTATE_SM); + dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); + dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; @@ -14795,8 +14918,18 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } } + s->is_nonstreaming = false; + if (s->sme_trap_nonstreaming) { + disas_sme_fa64(s, insn); + } + switch (extract32(insn, 25, 4)) { - case 0x0: case 0x1: case 0x3: /* UNALLOCATED */ + case 0x0: + if (!extract32(insn, 31, 1) || !disas_sme(s, insn)) { + unallocated_encoding(s); + } + break; + case 0x1: case 0x3: /* UNALLOCATED */ unallocated_encoding(s); break; case 0x2: diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h index dbc917ee65b..ad3762d1acf 100644 --- a/target/arm/translate-a64.h +++ b/target/arm/translate-a64.h @@ -29,6 +29,27 @@ void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v); bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, unsigned int imms, unsigned int immr); bool sve_access_check(DisasContext *s); +bool sme_enabled_check(DisasContext *s); +bool sme_enabled_check_with_svcr(DisasContext *s, unsigned); + +/* This function corresponds to CheckStreamingSVEEnabled. */ +static inline bool sme_sm_enabled_check(DisasContext *s) +{ + return sme_enabled_check_with_svcr(s, R_SVCR_SM_MASK); +} + +/* This function corresponds to CheckSMEAndZAEnabled. */ +static inline bool sme_za_enabled_check(DisasContext *s) +{ + return sme_enabled_check_with_svcr(s, R_SVCR_ZA_MASK); +} + +/* Note that this function corresponds to CheckStreamingSVEAndZAEnabled. */ +static inline bool sme_smza_enabled_check(DisasContext *s) +{ + return sme_enabled_check_with_svcr(s, R_SVCR_SM_MASK | R_SVCR_ZA_MASK); +} + TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr); TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, bool tag_checked, int log2_size); @@ -107,7 +128,66 @@ static inline int vec_full_reg_size(DisasContext *s) return s->vl; } +/* Return the byte size of the vector register, SVL / 8. */ +static inline int streaming_vec_reg_size(DisasContext *s) +{ + return s->svl; +} + +/* + * Return the offset info CPUARMState of the predicate vector register Pn. + * Note for this purpose, FFR is P16. + */ +static inline int pred_full_reg_offset(DisasContext *s, int regno) +{ + return offsetof(CPUARMState, vfp.pregs[regno]); +} + +/* Return the byte size of the whole predicate register, VL / 64. */ +static inline int pred_full_reg_size(DisasContext *s) +{ + return s->vl >> 3; +} + +/* Return the byte size of the predicate register, SVL / 64. */ +static inline int streaming_pred_reg_size(DisasContext *s) +{ + return s->svl >> 3; +} + +/* + * Round up the size of a register to a size allowed by + * the tcg vector infrastructure. Any operation which uses this + * size may assume that the bits above pred_full_reg_size are zero, + * and must leave them the same way. + * + * Note that this is not needed for the vector registers as they + * are always properly sized for tcg vectors. + */ +static inline int size_for_gvec(int size) +{ + if (size <= 8) { + return 8; + } else { + return QEMU_ALIGN_UP(size, 16); + } +} + +static inline int pred_gvec_reg_size(DisasContext *s) +{ + return size_for_gvec(pred_full_reg_size(s)); +} + +/* Return a newly allocated pointer to the predicate register. */ +static inline TCGv_ptr pred_full_reg_ptr(DisasContext *s, int regno) +{ + TCGv_ptr ret = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ret, cpu_env, pred_full_reg_offset(s, regno)); + return ret; +} + bool disas_sve(DisasContext *, uint32_t); +bool disas_sme(DisasContext *, uint32_t); void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); @@ -115,4 +195,7 @@ void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); +void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); + #endif /* TARGET_ARM_TRANSLATE_A64_H */ diff --git a/target/arm/translate-sme.c b/target/arm/translate-sme.c new file mode 100644 index 00000000000..7b87a9df63c --- /dev/null +++ b/target/arm/translate-sme.c @@ -0,0 +1,373 @@ +/* + * AArch64 SME translation + * + * Copyright (c) 2022 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "tcg/tcg-gvec-desc.h" +#include "translate.h" +#include "exec/helper-gen.h" +#include "translate-a64.h" +#include "fpu/softfloat.h" + + +/* + * Include the generated decoder. + */ + +#include "decode-sme.c.inc" + + +/* + * Resolve tile.size[index] to a host pointer, where tile and index + * are always decoded together, dependent on the element size. + */ +static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, + int tile_index, bool vertical) +{ + int tile = tile_index >> (4 - esz); + int index = esz == MO_128 ? 0 : extract32(tile_index, 0, 4 - esz); + int pos, len, offset; + TCGv_i32 tmp; + TCGv_ptr addr; + + /* Compute the final index, which is Rs+imm. */ + tmp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(tmp, cpu_reg(s, rs)); + tcg_gen_addi_i32(tmp, tmp, index); + + /* Prepare a power-of-two modulo via extraction of @len bits. */ + len = ctz32(streaming_vec_reg_size(s)) - esz; + + if (vertical) { + /* + * Compute the byte offset of the index within the tile: + * (index % (svl / size)) * size + * = (index % (svl >> esz)) << esz + * Perform the power-of-two modulo via extraction of the low @len bits. + * Perform the multiply by shifting left by @pos bits. + * Perform these operations simultaneously via deposit into zero. + */ + pos = esz; + tcg_gen_deposit_z_i32(tmp, tmp, pos, len); + + /* + * For big-endian, adjust the indexed column byte offset within + * the uint64_t host words that make up env->zarray[]. + */ + if (HOST_BIG_ENDIAN && esz < MO_64) { + tcg_gen_xori_i32(tmp, tmp, 8 - (1 << esz)); + } + } else { + /* + * Compute the byte offset of the index within the tile: + * (index % (svl / size)) * (size * sizeof(row)) + * = (index % (svl >> esz)) << (esz + log2(sizeof(row))) + */ + pos = esz + ctz32(sizeof(ARMVectorReg)); + tcg_gen_deposit_z_i32(tmp, tmp, pos, len); + + /* Row slices are always aligned and need no endian adjustment. */ + } + + /* The tile byte offset within env->zarray is the row. */ + offset = tile * sizeof(ARMVectorReg); + + /* Include the byte offset of zarray to make this relative to env. */ + offset += offsetof(CPUARMState, zarray); + tcg_gen_addi_i32(tmp, tmp, offset); + + /* Add the byte offset to env to produce the final pointer. */ + addr = tcg_temp_new_ptr(); + tcg_gen_ext_i32_ptr(addr, tmp); + tcg_temp_free_i32(tmp); + tcg_gen_add_ptr(addr, addr, cpu_env); + + return addr; +} + +static bool trans_ZERO(DisasContext *s, arg_ZERO *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_za_enabled_check(s)) { + gen_helper_sme_zero(cpu_env, tcg_constant_i32(a->imm), + tcg_constant_i32(streaming_vec_reg_size(s))); + } + return true; +} + +static bool trans_MOVA(DisasContext *s, arg_MOVA *a) +{ + static gen_helper_gvec_4 * const h_fns[5] = { + gen_helper_sve_sel_zpzz_b, gen_helper_sve_sel_zpzz_h, + gen_helper_sve_sel_zpzz_s, gen_helper_sve_sel_zpzz_d, + gen_helper_sve_sel_zpzz_q + }; + static gen_helper_gvec_3 * const cz_fns[5] = { + gen_helper_sme_mova_cz_b, gen_helper_sme_mova_cz_h, + gen_helper_sme_mova_cz_s, gen_helper_sme_mova_cz_d, + gen_helper_sme_mova_cz_q, + }; + static gen_helper_gvec_3 * const zc_fns[5] = { + gen_helper_sme_mova_zc_b, gen_helper_sme_mova_zc_h, + gen_helper_sme_mova_zc_s, gen_helper_sme_mova_zc_d, + gen_helper_sme_mova_zc_q, + }; + + TCGv_ptr t_za, t_zr, t_pg; + TCGv_i32 t_desc; + int svl; + + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (!sme_smza_enabled_check(s)) { + return true; + } + + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za_imm, a->v); + t_zr = vec_full_reg_ptr(s, a->zr); + t_pg = pred_full_reg_ptr(s, a->pg); + + svl = streaming_vec_reg_size(s); + t_desc = tcg_constant_i32(simd_desc(svl, svl, 0)); + + if (a->v) { + /* Vertical slice -- use sme mova helpers. */ + if (a->to_vec) { + zc_fns[a->esz](t_zr, t_za, t_pg, t_desc); + } else { + cz_fns[a->esz](t_za, t_zr, t_pg, t_desc); + } + } else { + /* Horizontal slice -- reuse sve sel helpers. */ + if (a->to_vec) { + h_fns[a->esz](t_zr, t_za, t_zr, t_pg, t_desc); + } else { + h_fns[a->esz](t_za, t_zr, t_za, t_pg, t_desc); + } + } + + tcg_temp_free_ptr(t_za); + tcg_temp_free_ptr(t_zr); + tcg_temp_free_ptr(t_pg); + + return true; +} + +static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) +{ + typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i32); + + /* + * Indexed by [esz][be][v][mte][st], which is (except for load/store) + * also the order in which the elements appear in the function names, + * and so how we must concatenate the pieces. + */ + +#define FN_LS(F) { gen_helper_sme_ld1##F, gen_helper_sme_st1##F } +#define FN_MTE(F) { FN_LS(F), FN_LS(F##_mte) } +#define FN_HV(F) { FN_MTE(F##_h), FN_MTE(F##_v) } +#define FN_END(L, B) { FN_HV(L), FN_HV(B) } + + static GenLdSt1 * const fns[5][2][2][2][2] = { + FN_END(b, b), + FN_END(h_le, h_be), + FN_END(s_le, s_be), + FN_END(d_le, d_be), + FN_END(q_le, q_be), + }; + +#undef FN_LS +#undef FN_MTE +#undef FN_HV +#undef FN_END + + TCGv_ptr t_za, t_pg; + TCGv_i64 addr; + int svl, desc = 0; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (!sme_smza_enabled_check(s)) { + return true; + } + + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za_imm, a->v); + t_pg = pred_full_reg_ptr(s, a->pg); + addr = tcg_temp_new_i64(); + + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->esz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + + if (mte) { + 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 { + 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, + tcg_constant_i32(desc)); + + tcg_temp_free_ptr(t_za); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i64(addr); + return true; +} + +typedef void GenLdStR(DisasContext *, TCGv_ptr, int, int, int, int); + +static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) +{ + int svl = streaming_vec_reg_size(s); + int imm = a->imm; + TCGv_ptr base; + + if (!sme_za_enabled_check(s)) { + return true; + } + + /* ZA[n] equates to ZA0H.B[n]. */ + base = get_tile_rowcol(s, MO_8, a->rv, imm, false); + + fn(s, base, 0, svl, a->rn, imm * svl); + + tcg_temp_free_ptr(base); + return true; +} + +TRANS_FEAT(LDR, aa64_sme, do_ldst_r, a, gen_sve_ldr) +TRANS_FEAT(STR, aa64_sme, do_ldst_r, a, gen_sve_str) + +static bool do_adda(DisasContext *s, arg_adda *a, MemOp esz, + gen_helper_gvec_4 *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, 0); + TCGv_ptr za, zn, pn, pm; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + /* Sum XZR+zad to find ZAd. */ + za = get_tile_rowcol(s, esz, 31, a->zad, false); + zn = vec_full_reg_ptr(s, a->zn); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + + fn(za, zn, pn, pm, tcg_constant_i32(desc)); + + tcg_temp_free_ptr(za); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(pn); + tcg_temp_free_ptr(pm); + return true; +} + +TRANS_FEAT(ADDHA_s, aa64_sme, do_adda, a, MO_32, gen_helper_sme_addha_s) +TRANS_FEAT(ADDVA_s, aa64_sme, do_adda, a, MO_32, gen_helper_sme_addva_s) +TRANS_FEAT(ADDHA_d, aa64_sme_i16i64, do_adda, a, MO_64, gen_helper_sme_addha_d) +TRANS_FEAT(ADDVA_d, aa64_sme_i16i64, do_adda, a, MO_64, gen_helper_sme_addva_d) + +static bool do_outprod(DisasContext *s, arg_op *a, MemOp esz, + gen_helper_gvec_5 *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->sub); + TCGv_ptr za, zn, zm, pn, pm; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + /* Sum XZR+zad to find ZAd. */ + za = get_tile_rowcol(s, esz, 31, a->zad, false); + zn = vec_full_reg_ptr(s, a->zn); + zm = vec_full_reg_ptr(s, a->zm); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + + fn(za, zn, zm, pn, pm, tcg_constant_i32(desc)); + + tcg_temp_free_ptr(za); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(pn); + tcg_temp_free_ptr(pm); + return true; +} + +static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, + gen_helper_gvec_5_ptr *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->sub); + TCGv_ptr za, zn, zm, pn, pm, fpst; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + /* Sum XZR+zad to find ZAd. */ + za = get_tile_rowcol(s, esz, 31, a->zad, false); + zn = vec_full_reg_ptr(s, a->zn); + zm = vec_full_reg_ptr(s, a->zm); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + fpst = fpstatus_ptr(FPST_FPCR); + + fn(za, zn, zm, pn, pm, fpst, tcg_constant_i32(desc)); + + tcg_temp_free_ptr(za); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(pn); + tcg_temp_free_ptr(pm); + tcg_temp_free_ptr(fpst); + return true; +} + +TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_h) +TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_s) +TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, gen_helper_sme_fmopa_d) + +/* TODO: FEAT_EBF16 */ +TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa) + +TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s) +TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s) +TRANS_FEAT(SUMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_sumopa_s) +TRANS_FEAT(USMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_usmopa_s) + +TRANS_FEAT(SMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_smopa_d) +TRANS_FEAT(UMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_umopa_d) +TRANS_FEAT(SUMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_sumopa_d) +TRANS_FEAT(USMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_usmopa_d) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 67761bf2cc5..41f8b12259e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -100,42 +100,6 @@ static inline int msz_dtype(DisasContext *s, int msz) * Implement all of the translator functions referenced by the decoder. */ -/* Return the offset info CPUARMState of the predicate vector register Pn. - * Note for this purpose, FFR is P16. - */ -static inline int pred_full_reg_offset(DisasContext *s, int regno) -{ - return offsetof(CPUARMState, vfp.pregs[regno]); -} - -/* Return the byte size of the whole predicate register, VL / 64. */ -static inline int pred_full_reg_size(DisasContext *s) -{ - return s->vl >> 3; -} - -/* Round up the size of a register to a size allowed by - * the tcg vector infrastructure. Any operation which uses this - * size may assume that the bits above pred_full_reg_size are zero, - * and must leave them the same way. - * - * Note that this is not needed for the vector registers as they - * are always properly sized for tcg vectors. - */ -static int size_for_gvec(int size) -{ - if (size <= 8) { - return 8; - } else { - return QEMU_ALIGN_UP(size, 16); - } -} - -static int pred_gvec_reg_size(DisasContext *s) -{ - return size_for_gvec(pred_full_reg_size(s)); -} - /* Invoke an out-of-line helper on 2 Zregs. */ static bool gen_gvec_ool_zz(DisasContext *s, gen_helper_gvec_2 *fn, int rd, int rn, int data) @@ -1322,6 +1286,19 @@ static bool trans_ADDVL(DisasContext *s, arg_ADDVL *a) return true; } +static bool trans_ADDSVL(DisasContext *s, arg_ADDSVL *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_enabled_check(s)) { + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * streaming_vec_reg_size(s)); + } + return true; +} + static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a) { if (!dc_isar_feature(aa64_sve, s)) { @@ -1335,6 +1312,19 @@ static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a) return true; } +static bool trans_ADDSPL(DisasContext *s, arg_ADDSPL *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_enabled_check(s)) { + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * streaming_pred_reg_size(s)); + } + return true; +} + static bool trans_RDVL(DisasContext *s, arg_RDVL *a) { if (!dc_isar_feature(aa64_sve, s)) { @@ -1347,6 +1337,18 @@ static bool trans_RDVL(DisasContext *s, arg_RDVL *a) return true; } +static bool trans_RDSVL(DisasContext *s, arg_RDSVL *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_enabled_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + tcg_gen_movi_i64(reg, a->imm * streaming_vec_reg_size(s)); + } + return true; +} + /* *** SVE Compute Vector Address Group */ @@ -1356,10 +1358,10 @@ static bool do_adr(DisasContext *s, arg_rrri *a, gen_helper_gvec_3 *fn) return gen_gvec_ool_zzz(s, fn, a->rd, a->rn, a->rm, a->imm); } -TRANS_FEAT(ADR_p32, aa64_sve, do_adr, a, gen_helper_sve_adr_p32) -TRANS_FEAT(ADR_p64, aa64_sve, do_adr, a, gen_helper_sve_adr_p64) -TRANS_FEAT(ADR_s32, aa64_sve, do_adr, a, gen_helper_sve_adr_s32) -TRANS_FEAT(ADR_u32, aa64_sve, do_adr, a, gen_helper_sve_adr_u32) +TRANS_FEAT_NONSTREAMING(ADR_p32, aa64_sve, do_adr, a, gen_helper_sve_adr_p32) +TRANS_FEAT_NONSTREAMING(ADR_p64, aa64_sve, do_adr, a, gen_helper_sve_adr_p64) +TRANS_FEAT_NONSTREAMING(ADR_s32, aa64_sve, do_adr, a, gen_helper_sve_adr_s32) +TRANS_FEAT_NONSTREAMING(ADR_u32, aa64_sve, do_adr, a, gen_helper_sve_adr_u32) /* *** SVE Integer Misc - Unpredicated Group @@ -1369,14 +1371,15 @@ static gen_helper_gvec_2 * const fexpa_fns[4] = { NULL, gen_helper_sve_fexpa_h, gen_helper_sve_fexpa_s, gen_helper_sve_fexpa_d, }; -TRANS_FEAT(FEXPA, aa64_sve, gen_gvec_ool_zz, - fexpa_fns[a->esz], a->rd, a->rn, 0) +TRANS_FEAT_NONSTREAMING(FEXPA, aa64_sve, gen_gvec_ool_zz, + fexpa_fns[a->esz], a->rd, a->rn, 0) static gen_helper_gvec_3 * const ftssel_fns[4] = { NULL, gen_helper_sve_ftssel_h, gen_helper_sve_ftssel_s, gen_helper_sve_ftssel_d, }; -TRANS_FEAT(FTSSEL, aa64_sve, gen_gvec_ool_arg_zzz, ftssel_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(FTSSEL, aa64_sve, gen_gvec_ool_arg_zzz, + ftssel_fns[a->esz], a, 0) /* *** SVE Predicate Logical Operations Group @@ -1821,7 +1824,8 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) TRANS_FEAT(PTRUE, aa64_sve, do_predset, a->esz, a->rd, a->pat, a->s) /* Note pat == 31 is #all, to set all elements. */ -TRANS_FEAT(SETFFR, aa64_sve, do_predset, 0, FFR_PRED_NUM, 31, false) +TRANS_FEAT_NONSTREAMING(SETFFR, aa64_sve, + do_predset, 0, FFR_PRED_NUM, 31, false) /* Note pat == 32 is #unimp, to set no elements. */ TRANS_FEAT(PFALSE, aa64_sve, do_predset, 0, a->rd, 32, false) @@ -1835,11 +1839,13 @@ static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a) .rd = a->rd, .pg = a->pg, .s = a->s, .rn = FFR_PRED_NUM, .rm = FFR_PRED_NUM, }; + + s->is_nonstreaming = true; return trans_AND_pppp(s, &alt_a); } -TRANS_FEAT(RDFFR, aa64_sve, do_mov_p, a->rd, FFR_PRED_NUM) -TRANS_FEAT(WRFFR, aa64_sve, do_mov_p, FFR_PRED_NUM, a->rn) +TRANS_FEAT_NONSTREAMING(RDFFR, aa64_sve, do_mov_p, a->rd, FFR_PRED_NUM) +TRANS_FEAT_NONSTREAMING(WRFFR, aa64_sve, do_mov_p, FFR_PRED_NUM, a->rn) static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, void (*gen_fn)(TCGv_i32, TCGv_ptr, @@ -2569,7 +2575,8 @@ TRANS_FEAT(TRN2_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, static gen_helper_gvec_3 * const compact_fns[4] = { NULL, NULL, gen_helper_sve_compact_s, gen_helper_sve_compact_d }; -TRANS_FEAT(COMPACT, aa64_sve, gen_gvec_ool_arg_zpz, compact_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(COMPACT, aa64_sve, gen_gvec_ool_arg_zpz, + compact_fns[a->esz], a, 0) /* Call the helper that computes the ARM LastActiveElement pseudocode * function, scaled by the element size. This includes the not found @@ -2894,6 +2901,8 @@ TRANS_FEAT(REVH, aa64_sve, gen_gvec_ool_arg_zpz, revh_fns[a->esz], a, 0) TRANS_FEAT(REVW, aa64_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_revw_d : NULL, a, 0) +TRANS_FEAT(REVD, aa64_sme, gen_gvec_ool_arg_zpz, gen_helper_sme_revd_q, a, 0) + TRANS_FEAT(SPLICE, aa64_sve, gen_gvec_ool_arg_zpzz, gen_helper_sve_splice, a, a->esz) @@ -3892,9 +3901,9 @@ static gen_helper_gvec_3_ptr * const ftmad_fns[4] = { NULL, gen_helper_sve_ftmad_h, gen_helper_sve_ftmad_s, gen_helper_sve_ftmad_d, }; -TRANS_FEAT(FTMAD, aa64_sve, gen_gvec_fpst_zzz, - ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) +TRANS_FEAT_NONSTREAMING(FTMAD, aa64_sve, gen_gvec_fpst_zzz, + ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) /* *** SVE Floating Point Accumulating Reduction Group @@ -3917,6 +3926,7 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) if (a->esz == 0 || !dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -3954,12 +3964,18 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) DO_FP3(FADD_zzz, fadd) DO_FP3(FSUB_zzz, fsub) DO_FP3(FMUL_zzz, fmul) -DO_FP3(FTSMUL, ftsmul) DO_FP3(FRECPS, recps) DO_FP3(FRSQRTS, rsqrts) #undef DO_FP3 +static gen_helper_gvec_3_ptr * const ftsmul_fns[4] = { + NULL, gen_helper_gvec_ftsmul_h, + gen_helper_gvec_ftsmul_s, gen_helper_gvec_ftsmul_d +}; +TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, + ftsmul_fns[a->esz], a, 0) + /* *** SVE Floating Point Arithmetic - Predicated Group */ @@ -4292,7 +4308,8 @@ TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, * The load should begin at the address Rn + IMM. */ -static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) +void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, + int len, int rn, int imm) { int len_align = QEMU_ALIGN_DOWN(len, 8); int len_remain = len % 8; @@ -4318,7 +4335,7 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) t0 = tcg_temp_new_i64(); for (i = 0; i < len_align; i += 8) { tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_st_i64(t0, cpu_env, vofs + i); + tcg_gen_st_i64(t0, base, vofs + i); tcg_gen_addi_i64(clean_addr, clean_addr, 8); } tcg_temp_free_i64(t0); @@ -4331,6 +4348,12 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) clean_addr = new_tmp_a64_local(s); tcg_gen_mov_i64(clean_addr, t0); + if (base != cpu_env) { + TCGv_ptr b = tcg_temp_local_new_ptr(); + tcg_gen_mov_ptr(b, base); + base = b; + } + gen_set_label(loop); t0 = tcg_temp_new_i64(); @@ -4338,7 +4361,7 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) tcg_gen_addi_i64(clean_addr, clean_addr, 8); tp = tcg_temp_new_ptr(); - tcg_gen_add_ptr(tp, cpu_env, i); + tcg_gen_add_ptr(tp, base, i); tcg_gen_addi_ptr(i, i, 8); tcg_gen_st_i64(t0, tp, vofs); tcg_temp_free_ptr(tp); @@ -4346,6 +4369,11 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); tcg_temp_free_ptr(i); + + if (base != cpu_env) { + tcg_temp_free_ptr(base); + assert(len_remain == 0); + } } /* @@ -4374,13 +4402,14 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) default: g_assert_not_reached(); } - tcg_gen_st_i64(t0, cpu_env, vofs + len_align); + tcg_gen_st_i64(t0, base, vofs + len_align); tcg_temp_free_i64(t0); } } /* Similarly for stores. */ -static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) +void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, + int len, int rn, int imm) { int len_align = QEMU_ALIGN_DOWN(len, 8); int len_remain = len % 8; @@ -4406,7 +4435,7 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) t0 = tcg_temp_new_i64(); for (i = 0; i < len_align; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, vofs + i); + tcg_gen_ld_i64(t0, base, vofs + i); tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ); tcg_gen_addi_i64(clean_addr, clean_addr, 8); } @@ -4420,11 +4449,17 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) clean_addr = new_tmp_a64_local(s); tcg_gen_mov_i64(clean_addr, t0); + if (base != cpu_env) { + TCGv_ptr b = tcg_temp_local_new_ptr(); + tcg_gen_mov_ptr(b, base); + base = b; + } + gen_set_label(loop); t0 = tcg_temp_new_i64(); tp = tcg_temp_new_ptr(); - tcg_gen_add_ptr(tp, cpu_env, i); + tcg_gen_add_ptr(tp, base, i); tcg_gen_ld_i64(t0, tp, vofs); tcg_gen_addi_ptr(i, i, 8); tcg_temp_free_ptr(tp); @@ -4435,12 +4470,17 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); tcg_temp_free_ptr(i); + + if (base != cpu_env) { + tcg_temp_free_ptr(base); + assert(len_remain == 0); + } } /* Predicate register stores can be any multiple of 2. */ if (len_remain) { t0 = tcg_temp_new_i64(); - tcg_gen_ld_i64(t0, cpu_env, vofs + len_align); + tcg_gen_ld_i64(t0, base, vofs + len_align); switch (len_remain) { case 2: @@ -4472,7 +4512,7 @@ static bool trans_LDR_zri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = vec_full_reg_size(s); int off = vec_full_reg_offset(s, a->rd); - do_ldr(s, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, cpu_env, off, size, a->rn, a->imm * size); } return true; } @@ -4485,7 +4525,7 @@ static bool trans_LDR_pri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = pred_full_reg_size(s); int off = pred_full_reg_offset(s, a->rd); - do_ldr(s, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, cpu_env, off, size, a->rn, a->imm * size); } return true; } @@ -4498,7 +4538,7 @@ static bool trans_STR_zri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = vec_full_reg_size(s); int off = vec_full_reg_offset(s, a->rd); - do_str(s, off, size, a->rn, a->imm * size); + gen_sve_str(s, cpu_env, off, size, a->rn, a->imm * size); } return true; } @@ -4511,7 +4551,7 @@ static bool trans_STR_pri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = pred_full_reg_size(s); int off = pred_full_reg_offset(s, a->rd); - do_str(s, off, size, a->rn, a->imm * size); + gen_sve_str(s, cpu_env, off, size, a->rn, a->imm * size); } return true; } @@ -4829,6 +4869,7 @@ static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); @@ -4930,6 +4971,7 @@ static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); int elements = vsz >> dtype_esz[a->dtype]; @@ -5084,6 +5126,7 @@ static bool trans_LD1RO_zprr(DisasContext *s, arg_rprr_load *a) if (a->rm == 31) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); @@ -5098,6 +5141,7 @@ static bool trans_LD1RO_zpri(DisasContext *s, arg_rpri_load *a) if (!dc_isar_feature(aa64_sve_f64mm, s)) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), a->imm * 32); @@ -5693,6 +5737,7 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5724,6 +5769,7 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5758,6 +5804,7 @@ static bool trans_LDNT1_zprz(DisasContext *s, arg_LD1_zprz *a) if (!dc_isar_feature(aa64_sve2, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5881,6 +5928,7 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5911,6 +5959,7 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5945,6 +5994,7 @@ static bool trans_STNT1_zprz(DisasContext *s, arg_ST1_zprz *a) if (!dc_isar_feature(aa64_sve2, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5989,6 +6039,17 @@ static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a) return true; } +static bool trans_PRF_ns(DisasContext *s, arg_PRF_ns *a) +{ + if (!dc_isar_feature(aa64_sve, s)) { + return false; + } + /* Prefetch is a nop within QEMU. */ + s->is_nonstreaming = true; + (void)sve_access_check(s); + return true; +} + /* * Move Prefix * @@ -6217,9 +6278,13 @@ static bool do_trans_pmull(DisasContext *s, arg_rrr_esz *a, bool sel) gen_helper_gvec_pmull_q, gen_helper_sve2_pmull_h, NULL, gen_helper_sve2_pmull_d, }; - if (a->esz == 0 - ? !dc_isar_feature(aa64_sve2_pmull128, s) - : !dc_isar_feature(aa64_sve, s)) { + + if (a->esz == 0) { + if (!dc_isar_feature(aa64_sve2_pmull128, s)) { + return false; + } + s->is_nonstreaming = true; + } else if (!dc_isar_feature(aa64_sve, s)) { return false; } return gen_gvec_ool_arg_zzz(s, fns[a->esz], a, sel); @@ -6407,22 +6472,22 @@ static gen_helper_gvec_3 * const bext_fns[4] = { gen_helper_sve2_bext_b, gen_helper_sve2_bext_h, gen_helper_sve2_bext_s, gen_helper_sve2_bext_d, }; -TRANS_FEAT(BEXT, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, - bext_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(BEXT, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, + bext_fns[a->esz], a, 0) static gen_helper_gvec_3 * const bdep_fns[4] = { gen_helper_sve2_bdep_b, gen_helper_sve2_bdep_h, gen_helper_sve2_bdep_s, gen_helper_sve2_bdep_d, }; -TRANS_FEAT(BDEP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, - bdep_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(BDEP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, + bdep_fns[a->esz], a, 0) static gen_helper_gvec_3 * const bgrp_fns[4] = { gen_helper_sve2_bgrp_b, gen_helper_sve2_bgrp_h, gen_helper_sve2_bgrp_s, gen_helper_sve2_bgrp_d, }; -TRANS_FEAT(BGRP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, - bgrp_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(BGRP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, + bgrp_fns[a->esz], a, 0) static gen_helper_gvec_3 * const cadd_fns[4] = { gen_helper_sve2_cadd_b, gen_helper_sve2_cadd_h, @@ -7130,21 +7195,21 @@ DO_SVE2_ZZZ_NARROW(RSUBHNT, rsubhnt) static gen_helper_gvec_flags_4 * const match_fns[4] = { gen_helper_sve2_match_ppzz_b, gen_helper_sve2_match_ppzz_h, NULL, NULL }; -TRANS_FEAT(MATCH, aa64_sve2, do_ppzz_flags, a, match_fns[a->esz]) +TRANS_FEAT_NONSTREAMING(MATCH, aa64_sve2, do_ppzz_flags, a, match_fns[a->esz]) static gen_helper_gvec_flags_4 * const nmatch_fns[4] = { gen_helper_sve2_nmatch_ppzz_b, gen_helper_sve2_nmatch_ppzz_h, NULL, NULL }; -TRANS_FEAT(NMATCH, aa64_sve2, do_ppzz_flags, a, nmatch_fns[a->esz]) +TRANS_FEAT_NONSTREAMING(NMATCH, aa64_sve2, do_ppzz_flags, a, nmatch_fns[a->esz]) static gen_helper_gvec_4 * const histcnt_fns[4] = { NULL, NULL, gen_helper_sve2_histcnt_s, gen_helper_sve2_histcnt_d }; -TRANS_FEAT(HISTCNT, aa64_sve2, gen_gvec_ool_arg_zpzz, - histcnt_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(HISTCNT, aa64_sve2, gen_gvec_ool_arg_zpzz, + histcnt_fns[a->esz], a, 0) -TRANS_FEAT(HISTSEG, aa64_sve2, gen_gvec_ool_arg_zzz, - a->esz == 0 ? gen_helper_sve2_histseg : NULL, a, 0) +TRANS_FEAT_NONSTREAMING(HISTSEG, aa64_sve2, gen_gvec_ool_arg_zzz, + a->esz == 0 ? gen_helper_sve2_histseg : NULL, a, 0) DO_ZPZZ_FP(FADDP, aa64_sve2, sve2_faddp_zpzz) DO_ZPZZ_FP(FMAXNMP, aa64_sve2, sve2_fmaxnmp_zpzz) @@ -7156,10 +7221,12 @@ DO_ZPZZ_FP(FMINP, aa64_sve2, sve2_fminp_zpzz) * SVE Integer Multiply-Add (unpredicated) */ -TRANS_FEAT(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_s, - a->rd, a->rn, a->rm, a->ra, 0, FPST_FPCR) -TRANS_FEAT(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_d, - a->rd, a->rn, a->rm, a->ra, 0, FPST_FPCR) +TRANS_FEAT_NONSTREAMING(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, + gen_helper_fmmla_s, a->rd, a->rn, a->rm, a->ra, + 0, FPST_FPCR) +TRANS_FEAT_NONSTREAMING(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, + gen_helper_fmmla_d, a->rd, a->rn, a->rm, a->ra, + 0, FPST_FPCR) static gen_helper_gvec_4 * const sqdmlal_zzzw_fns[] = { NULL, gen_helper_sve2_sqdmlal_zzzw_h, @@ -7256,20 +7323,21 @@ TRANS_FEAT(SQRDCMLAH_zzzz, aa64_sve2, gen_gvec_ool_zzzz, TRANS_FEAT(USDOT_zzzz, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, a->esz == 2 ? gen_helper_gvec_usdot_b : NULL, a, 0) -TRANS_FEAT(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, - gen_helper_crypto_aesmc, a->rd, a->rd, a->decrypt) +TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, + gen_helper_crypto_aesmc, a->rd, a->rd, a->decrypt) -TRANS_FEAT(AESE, aa64_sve2_aes, gen_gvec_ool_arg_zzz, - gen_helper_crypto_aese, a, false) -TRANS_FEAT(AESD, aa64_sve2_aes, gen_gvec_ool_arg_zzz, - gen_helper_crypto_aese, a, true) +TRANS_FEAT_NONSTREAMING(AESE, aa64_sve2_aes, gen_gvec_ool_arg_zzz, + gen_helper_crypto_aese, a, false) +TRANS_FEAT_NONSTREAMING(AESD, aa64_sve2_aes, gen_gvec_ool_arg_zzz, + gen_helper_crypto_aese, a, true) -TRANS_FEAT(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, - gen_helper_crypto_sm4e, a, 0) -TRANS_FEAT(SM4EKEY, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, - gen_helper_crypto_sm4ekey, a, 0) +TRANS_FEAT_NONSTREAMING(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, + gen_helper_crypto_sm4e, a, 0) +TRANS_FEAT_NONSTREAMING(SM4EKEY, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, + gen_helper_crypto_sm4ekey, a, 0) -TRANS_FEAT(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz, gen_gvec_rax1, a) +TRANS_FEAT_NONSTREAMING(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz, + gen_gvec_rax1, a) TRANS_FEAT(FCVTNT_sh, aa64_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_sh, a, 0, FPST_FPCR) @@ -7320,20 +7388,20 @@ TRANS_FEAT(FMLALT_zzxw, aa64_sve2, do_FMLAL_zzxw, a, false, true) TRANS_FEAT(FMLSLB_zzxw, aa64_sve2, do_FMLAL_zzxw, a, true, false) TRANS_FEAT(FMLSLT_zzxw, aa64_sve2, do_FMLAL_zzxw, a, true, true) -TRANS_FEAT(SMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_smmla_b, a, 0) -TRANS_FEAT(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_usmmla_b, a, 0) -TRANS_FEAT(UMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_ummla_b, a, 0) +TRANS_FEAT_NONSTREAMING(SMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_smmla_b, a, 0) +TRANS_FEAT_NONSTREAMING(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_usmmla_b, a, 0) +TRANS_FEAT_NONSTREAMING(UMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_ummla_b, a, 0) TRANS_FEAT(BFDOT_zzzz, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, gen_helper_gvec_bfdot, a, 0) TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_ool_arg_zzxz, gen_helper_gvec_bfdot_idx, a) -TRANS_FEAT(BFMMLA, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_bfmmla, a, 0) +TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_bfmmla, a, 0) static bool do_BFMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) { @@ -7353,3 +7421,162 @@ static bool do_BFMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) TRANS_FEAT(BFMLALB_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, false) TRANS_FEAT(BFMLALT_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, true) + +static bool trans_PSEL(DisasContext *s, arg_psel *a) +{ + int vl = vec_full_reg_size(s); + int pl = pred_gvec_reg_size(s); + int elements = vl >> a->esz; + TCGv_i64 tmp, didx, dbit; + TCGv_ptr ptr; + + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + tmp = tcg_temp_new_i64(); + dbit = tcg_temp_new_i64(); + didx = tcg_temp_new_i64(); + ptr = tcg_temp_new_ptr(); + + /* Compute the predicate element. */ + tcg_gen_addi_i64(tmp, cpu_reg(s, a->rv), a->imm); + if (is_power_of_2(elements)) { + tcg_gen_andi_i64(tmp, tmp, elements - 1); + } else { + tcg_gen_remu_i64(tmp, tmp, tcg_constant_i64(elements)); + } + + /* Extract the predicate byte and bit indices. */ + tcg_gen_shli_i64(tmp, tmp, a->esz); + tcg_gen_andi_i64(dbit, tmp, 7); + tcg_gen_shri_i64(didx, tmp, 3); + if (HOST_BIG_ENDIAN) { + tcg_gen_xori_i64(didx, didx, 7); + } + + /* Load the predicate word. */ + tcg_gen_trunc_i64_ptr(ptr, didx); + tcg_gen_add_ptr(ptr, ptr, cpu_env); + tcg_gen_ld8u_i64(tmp, ptr, pred_full_reg_offset(s, a->pm)); + + /* Extract the predicate bit and replicate to MO_64. */ + tcg_gen_shr_i64(tmp, tmp, dbit); + tcg_gen_andi_i64(tmp, tmp, 1); + tcg_gen_neg_i64(tmp, tmp); + + /* Apply to either copy the source, or write zeros. */ + tcg_gen_gvec_ands(MO_64, pred_full_reg_offset(s, a->pd), + pred_full_reg_offset(s, a->pn), tmp, pl, pl); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(dbit); + tcg_temp_free_i64(didx); + tcg_temp_free_ptr(ptr); + return true; +} + +static void gen_sclamp_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_i32 a) +{ + tcg_gen_smax_i32(d, a, n); + tcg_gen_smin_i32(d, d, m); +} + +static void gen_sclamp_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 a) +{ + tcg_gen_smax_i64(d, a, n); + tcg_gen_smin_i64(d, d, m); +} + +static void gen_sclamp_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec a) +{ + tcg_gen_smax_vec(vece, d, a, n); + tcg_gen_smin_vec(vece, d, d, m); +} + +static void gen_sclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop[] = { + INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_b, + .opt_opc = vecop, + .vece = MO_8 }, + { .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_h, + .opt_opc = vecop, + .vece = MO_16 }, + { .fni4 = gen_sclamp_i32, + .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_s, + .opt_opc = vecop, + .vece = MO_32 }, + { .fni8 = gen_sclamp_i64, + .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_d, + .opt_opc = vecop, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64 } + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &ops[vece]); +} + +TRANS_FEAT(SCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_sclamp, a) + +static void gen_uclamp_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_i32 a) +{ + tcg_gen_umax_i32(d, a, n); + tcg_gen_umin_i32(d, d, m); +} + +static void gen_uclamp_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 a) +{ + tcg_gen_umax_i64(d, a, n); + tcg_gen_umin_i64(d, d, m); +} + +static void gen_uclamp_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec a) +{ + tcg_gen_umax_vec(vece, d, a, n); + tcg_gen_umin_vec(vece, d, d, m); +} + +static void gen_uclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop[] = { + INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_b, + .opt_opc = vecop, + .vece = MO_8 }, + { .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_h, + .opt_opc = vecop, + .vece = MO_16 }, + { .fni4 = gen_uclamp_i32, + .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_s, + .opt_opc = vecop, + .vece = MO_32 }, + { .fni8 = gen_uclamp_i64, + .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_d, + .opt_opc = vecop, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64 } + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &ops[vece]); +} + +TRANS_FEAT(UCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_uclamp, a) diff --git a/target/arm/translate-vfp.c b/target/arm/translate-vfp.c index 82fdbcae530..bd5ae27d090 100644 --- a/target/arm/translate-vfp.c +++ b/target/arm/translate-vfp.c @@ -234,6 +234,18 @@ static bool vfp_access_check_a(DisasContext *s, bool ignore_vfp_enabled) return false; } + /* + * Note that rebuild_hflags_a32 has already accounted for being in EL0 + * and the higher EL in A64 mode, etc. Unlike A64 mode, there do not + * appear to be any insns which touch VFP which are allowed. + */ + if (s->sme_trap_nonstreaming) { + gen_exception_insn(s, s->pc_curr, EXCP_UDEF, + syn_smetrap(SME_ET_Streaming, + s->base.pc_next - s->pc_curr == 2)); + return false; + } + if (!s->vfp_enabled && !ignore_vfp_enabled) { assert(!arm_dc_feature(s, ARM_FEATURE_M)); unallocated_encoding(s); diff --git a/target/arm/translate.c b/target/arm/translate.c index 3e2f40daab0..8dc4b435309 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -9420,6 +9420,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN); dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); } + dc->sme_trap_nonstreaming = + EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); } dc->cp_regs = cpu->cp_regs; dc->features = env->features; diff --git a/target/arm/translate.h b/target/arm/translate.h index 88dc18a034b..af5d4a7086f 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -42,7 +42,9 @@ typedef struct DisasContext { bool ns; /* Use non-secure CPREG bank on access */ int fp_excp_el; /* FP exception EL or 0 if enabled */ int sve_excp_el; /* SVE exception EL or 0 if enabled */ + int sme_excp_el; /* SME exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ + int svl; /* current streaming vector length in bytes */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; int vec_stride; @@ -96,6 +98,14 @@ typedef struct DisasContext { bool align_mem; /* True if PSTATE.IL is set */ bool pstate_il; + /* True if PSTATE.SM is set. */ + bool pstate_sm; + /* True if PSTATE.ZA is set. */ + bool pstate_za; + /* True if non-streaming insns should raise an SME Streaming exception. */ + bool sme_trap_nonstreaming; + /* True if the current instruction is non-streaming. */ + bool is_nonstreaming; /* True if MVE insns are definitely not predicated by VPR or LTPSIZE */ bool mve_no_pred; /* @@ -146,6 +156,11 @@ static inline int plus_2(DisasContext *s, int x) return x + 2; } +static inline int plus_12(DisasContext *s, int x) +{ + return x + 12; +} + static inline int times_2(DisasContext *s, int x) { return x * 2; @@ -556,4 +571,11 @@ uint64_t asimd_imm_const(uint32_t imm, int cmode, int op); static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ { return dc_isar_feature(FEAT, s) && FUNC(s, __VA_ARGS__); } +#define TRANS_FEAT_NONSTREAMING(NAME, FEAT, FUNC, ...) \ + static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ + { \ + s->is_nonstreaming = true; \ + return dc_isar_feature(FEAT, s) && FUNC(s, __VA_ARGS__); \ + } + #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 9a9c034e36f..f59d3b26eac 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -2690,3 +2690,27 @@ void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, } clear_tail(d, opr_sz, simd_maxsz(desc)); } + +#define DO_CLAMP(NAME, TYPE) \ +void HELPER(NAME)(void *d, void *n, void *m, void *a, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; i += sizeof(TYPE)) { \ + TYPE aa = *(TYPE *)(a + i); \ + TYPE nn = *(TYPE *)(n + i); \ + TYPE mm = *(TYPE *)(m + i); \ + TYPE dd = MIN(MAX(aa, nn), mm); \ + *(TYPE *)(d + i) = dd; \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_CLAMP(gvec_sclamp_b, int8_t) +DO_CLAMP(gvec_sclamp_h, int16_t) +DO_CLAMP(gvec_sclamp_s, int32_t) +DO_CLAMP(gvec_sclamp_d, int64_t) + +DO_CLAMP(gvec_uclamp_b, uint8_t) +DO_CLAMP(gvec_uclamp_h, uint16_t) +DO_CLAMP(gvec_uclamp_s, uint32_t) +DO_CLAMP(gvec_uclamp_d, uint64_t) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 5d70e34dd54..05b992ff730 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -214,7 +214,6 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) cc->has_work = avr_cpu_has_work; cc->dump_state = avr_cpu_dump_state; cc->set_pc = avr_cpu_set_pc; - cc->memory_rw_debug = avr_cpu_memory_rw_debug; dc->vmsd = &vms_avr_cpu; cc->sysemu_ops = &avr_sysemu_ops; cc->disas_set_info = avr_cpu_disas_set_info; diff --git a/target/avr/cpu.h b/target/avr/cpu.h index d304f33301b..96419c0c2be 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -184,8 +184,6 @@ void avr_cpu_tcg_init(void); void avr_cpu_list(void); int cpu_avr_exec(CPUState *cpu); -int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, - int len, bool is_write); enum { TB_FLAGS_FULL_ACCESS = 1, diff --git a/target/avr/helper.c b/target/avr/helper.c index c27f7029010..db76452f9a4 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -93,12 +93,6 @@ void avr_cpu_do_interrupt(CPUState *cs) cs->exception_index = -1; } -int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf, - int len, bool is_write) -{ - return cpu_memory_rw_debug(cs, addr, buf, len, is_write); -} - hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { return addr; /* I assume 1:1 address correspondence */ diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index c6f0879b6ee..50634ac4593 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -339,12 +339,13 @@ do { \ TCGv LSB = tcg_temp_local_new(); \ TCGLabel *label = gen_new_label(); \ - GET_EA; \ + tcg_gen_movi_tl(EA, 0); \ PRED; \ + CHECK_NOSHUF_PRED(GET_EA, SIZE, LSB); \ PRED_LOAD_CANCEL(LSB, EA); \ tcg_gen_movi_tl(RdV, 0); \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \ - fLOAD(1, SIZE, SIGN, EA, RdV); \ + fLOAD(1, SIZE, SIGN, EA, RdV); \ gen_set_label(label); \ tcg_temp_free(LSB); \ } while (0) @@ -398,12 +399,13 @@ do { \ TCGv LSB = tcg_temp_local_new(); \ TCGLabel *label = gen_new_label(); \ - GET_EA; \ + tcg_gen_movi_tl(EA, 0); \ PRED; \ + CHECK_NOSHUF_PRED(GET_EA, 8, LSB); \ PRED_LOAD_CANCEL(LSB, EA); \ tcg_gen_movi_i64(RddV, 0); \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \ - fLOAD(1, 8, u, EA, RddV); \ + fLOAD(1, 8, u, EA, RddV); \ gen_set_label(label); \ tcg_temp_free(LSB); \ } while (0) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index cd6af4bcebf..8a334ba07b6 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -638,5 +638,12 @@ static void vec_to_qvec(size_t size, intptr_t dstoff, intptr_t srcoff) tcg_temp_free_i64(mask); } +static 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); +} + #include "tcg_funcs_generated.c.inc" #include "tcg_func_table_generated.c.inc" diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index c89aa4ed4d6..368f0b57085 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -104,6 +104,7 @@ DEF_HELPER_1(vwhist128q, void, env) DEF_HELPER_2(vwhist128m, void, env, s32) DEF_HELPER_2(vwhist128qm, void, env, s32) +DEF_HELPER_4(probe_noshuf_load, void, env, i32, int, int) DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) DEF_HELPER_2(probe_hvx_stores, void, env, int) DEF_HELPER_3(probe_pkt_scalar_hvx_stores, void, env, int, int) diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index a78e84faa43..92eb8bbf05c 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -87,11 +87,28 @@ * * * For qemu, we look for a load in slot 0 when there is a store in slot 1 - * in the same packet. When we see this, we call a helper that merges the - * bytes from the store buffer with the value loaded from memory. + * in the same packet. When we see this, we call a helper that probes the + * load to make sure it doesn't fault. Then, we process the store ahead of + * the actual load. + */ -#define CHECK_NOSHUF \ +#define CHECK_NOSHUF(VA, SIZE) \ do { \ + if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ + probe_noshuf_load(VA, SIZE, ctx->mem_idx); \ + process_store(ctx, pkt, 1); \ + } \ + } while (0) + +#define CHECK_NOSHUF_PRED(GET_EA, SIZE, PRED) \ + do { \ + TCGLabel *label = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, label); \ + GET_EA; \ + if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ + probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ + } \ + gen_set_label(label); \ if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ process_store(ctx, pkt, 1); \ } \ @@ -99,37 +116,37 @@ #define MEM_LOAD1s(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 1); \ tcg_gen_qemu_ld8s(DST, VA, ctx->mem_idx); \ } while (0) #define MEM_LOAD1u(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 1); \ tcg_gen_qemu_ld8u(DST, VA, ctx->mem_idx); \ } while (0) #define MEM_LOAD2s(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 2); \ tcg_gen_qemu_ld16s(DST, VA, ctx->mem_idx); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 2); \ tcg_gen_qemu_ld16u(DST, VA, ctx->mem_idx); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 4); \ tcg_gen_qemu_ld32s(DST, VA, ctx->mem_idx); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 4); \ tcg_gen_qemu_ld32s(DST, VA, ctx->mem_idx); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ - CHECK_NOSHUF; \ + CHECK_NOSHUF(VA, 8); \ tcg_gen_qemu_ld64(DST, VA, ctx->mem_idx); \ } while (0) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index a5ed819c045..085afc32746 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -442,6 +442,17 @@ static void probe_store(CPUHexagonState *env, int slot, int mmu_idx) } } +/* + * Called from a mem_noshuf packet to make sure the load doesn't + * raise an exception + */ +void HELPER(probe_noshuf_load)(CPUHexagonState *env, target_ulong va, + int size, int mmu_idx) +{ + uintptr_t retaddr = GETPC(); + probe_read(env, va, size, mmu_idx, retaddr); +} + /* Called during packet commit when there are two scalar stores */ void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int mmu_idx) { @@ -514,10 +525,12 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask, * If the load is in slot 0 and there is a store in slot1 (that * wasn't cancelled), we have to do the store first. */ -static void check_noshuf(CPUHexagonState *env, uint32_t slot) +static void check_noshuf(CPUHexagonState *env, uint32_t slot, + target_ulong vaddr, int size) { if (slot == 0 && env->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); } } @@ -526,7 +539,7 @@ static uint8_t mem_load1(CPUHexagonState *env, uint32_t slot, target_ulong vaddr) { uintptr_t ra = GETPC(); - check_noshuf(env, slot); + check_noshuf(env, slot, vaddr, 1); return cpu_ldub_data_ra(env, vaddr, ra); } @@ -534,7 +547,7 @@ static uint16_t mem_load2(CPUHexagonState *env, uint32_t slot, target_ulong vaddr) { uintptr_t ra = GETPC(); - check_noshuf(env, slot); + check_noshuf(env, slot, vaddr, 2); return cpu_lduw_data_ra(env, vaddr, ra); } @@ -542,7 +555,7 @@ static uint32_t mem_load4(CPUHexagonState *env, uint32_t slot, target_ulong vaddr) { uintptr_t ra = GETPC(); - check_noshuf(env, slot); + check_noshuf(env, slot, vaddr, 4); return cpu_ldl_data_ra(env, vaddr, ra); } @@ -550,7 +563,7 @@ static uint64_t mem_load8(CPUHexagonState *env, uint32_t slot, target_ulong vaddr) { uintptr_t ra = GETPC(); - check_noshuf(env, slot); + check_noshuf(env, slot, vaddr, 8); return cpu_ldq_data_ra(env, vaddr, ra); } diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index f8833277ab6..8d2248bb3f6 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -221,6 +221,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); CPUX86State *env = &x86cpu->env; + uint64_t reqCap; init_emu(); init_decoder(); @@ -257,19 +258,26 @@ int hvf_arch_init_vcpu(CPUState *cpu) /* set VMCS control fields */ wvmcs(cpu->hvf->fd, VMCS_PIN_BASED_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_pinbased, - VMCS_PIN_BASED_CTLS_EXTINT | - VMCS_PIN_BASED_CTLS_NMI | - VMCS_PIN_BASED_CTLS_VNMI)); + VMCS_PIN_BASED_CTLS_EXTINT | + VMCS_PIN_BASED_CTLS_NMI | + VMCS_PIN_BASED_CTLS_VNMI)); wvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased, - VMCS_PRI_PROC_BASED_CTLS_HLT | - VMCS_PRI_PROC_BASED_CTLS_MWAIT | - VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET | - VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW) | + VMCS_PRI_PROC_BASED_CTLS_HLT | + VMCS_PRI_PROC_BASED_CTLS_MWAIT | + VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET | + VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW) | VMCS_PRI_PROC_BASED_CTLS_SEC_CONTROL); + + reqCap = VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES; + + /* Is RDTSCP support in CPUID? If so, enable it in the VMCS. */ + if (hvf_get_supported_cpuid(0x80000001, 0, R_EDX) & CPUID_EXT2_RDTSCP) { + reqCap |= VMCS_PRI_PROC_BASED2_CTLS_RDTSCP; + } + wvmcs(cpu->hvf->fd, VMCS_SEC_PROC_BASED_CTLS, - cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased2, - VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES)); + cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased2, reqCap)); wvmcs(cpu->hvf->fd, VMCS_ENTRY_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_entry, 0)); diff --git a/target/i386/hvf/vmcs.h b/target/i386/hvf/vmcs.h index b4692f63f65..aee6f75dfd5 100644 --- a/target/i386/hvf/vmcs.h +++ b/target/i386/hvf/vmcs.h @@ -354,7 +354,7 @@ #define VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET (1 << 3) #define VMCS_PRI_PROC_BASED_CTLS_HLT (1 << 7) #define VMCS_PRI_PROC_BASED_CTLS_MWAIT (1 << 10) -#define VMCS_PRI_PROC_BASED_CTLS_TSC (1 << 12) +#define VMCS_PRI_PROC_BASED_CTLS_RDTSC (1 << 12) #define VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD (1 << 19) #define VMCS_PRI_PROC_BASED_CTLS_CR8_STORE (1 << 20) #define VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW (1 << 21) @@ -362,6 +362,7 @@ #define VMCS_PRI_PROC_BASED_CTLS_SEC_CONTROL (1 << 31) #define VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES (1 << 0) +#define VMCS_PRI_PROC_BASED2_CTLS_RDTSCP (1 << 3) #define VMCS_PRI_PROC_BASED2_CTLS_X2APIC (1 << 4) enum task_switch_reason { diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index f24dd50e48f..7323a7a94b1 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -95,7 +95,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, ebx &= ~CPUID_7_0_EBX_INVPCID; } - ecx &= CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_AVX512_VPOPCNTDQ; + ecx &= CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_AVX512_VPOPCNTDQ | + CPUID_7_0_ECX_RDPID; edx &= CPUID_7_0_EDX_AVX512_4VNNIW | CPUID_7_0_EDX_AVX512_4FMAPS; } else { ebx = 0; @@ -132,11 +133,11 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, CPUID_FXSR | CPUID_EXT2_FXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX; hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap); - if (!(cap & CPU_BASED2_RDTSCP)) { + if (!(cap2ctrl(cap, CPU_BASED2_RDTSCP) & CPU_BASED2_RDTSCP)) { edx &= ~CPUID_EXT2_RDTSCP; } hv_vmx_read_capability(HV_VMX_CAP_PROCBASED, &cap); - if (!(cap & CPU_BASED_TSC_OFFSET)) { + if (!(cap2ctrl(cap, CPU_BASED_TSC_OFFSET) & CPU_BASED_TSC_OFFSET)) { edx &= ~CPUID_EXT2_RDTSCP; } ecx &= CPUID_EXT3_LAHF_LM | CPUID_EXT3_CMP_LEG | CPUID_EXT3_CR8LEG | diff --git a/target/loongarch/README b/target/loongarch/README index 4dcd0f1682c..9f5edd10c8e 100644 --- a/target/loongarch/README +++ b/target/loongarch/README @@ -24,9 +24,9 @@ Download cross-tools. - wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20211202-cross-tools.tar.xz + wget https://github.com/loongson/build-tools/releases/download/2022.05.29/loongarch64-clfs-5.0-cross-tools-gcc-full.tar.xz - tar -vxf loongarch64-clfs-20211202-cross-tools.tar.xz -C /opt + tar -vxf loongarch64-clfs-5.0-cross-tools-gcc-full.tar.xz -C /opt Config cross-tools env. @@ -60,5 +60,40 @@ ./build/qemu-system-loongarch64 -machine virt -m 4G -cpu Loongson-3A5000 -smp 1 -kernel build/tests/tcg/loongarch64-softmmu/hello -monitor none -display none -chardev file,path=hello.out,id=output -serial chardev:output +- Linux-user emulation + + We already support Linux user emulation. We can use LoongArch cross-tools to build LoongArch executables on X86 machines, + and We can also use qemu-loongarch64 to run LoongArch executables. + + 1. Config cross-tools env. + + see System emulation. + + 2. Test tests/tcg/multiarch. + + ./configure --static --prefix=/usr --disable-werror --target-list="loongarch64-linux-user" --enable-debug + + cd build + + make && make check-tcg + + 3. Run LoongArch system basic command with loongarch-clfs-system. + + - Config clfs env. + + wget https://github.com/loongson/build-tools/releases/download/2022.05.29/loongarch64-clfs-system-5.0.tar.bz2 + + tar -vxf loongarch64-clfs-system-5.0.tar.bz2 -C /opt/clfs + + cp /opt/clfs/lib64/ld-linux-loongarch-lp64d.so.1 /lib64 + + export LD_LIBRARY_PATH="/opt/clfs/lib64" + + - Run LoongArch system basic command. + + ./qemu-loongarch64 /opt/clfs/usr/bin/bash + ./qemu-loongarch64 /opt/clfs/usr/bin/ls + ./qemu-loongarch64 /opt/clfs/usr/bin/pwd + - Note. We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 4c8f96bc3a4..1c69a76f2ba 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -18,7 +18,6 @@ #include "fpu/softfloat-helpers.h" #include "cpu-csr.h" #include "sysemu/reset.h" -#include "hw/loader.h" const char * const regnames[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -51,6 +50,7 @@ static const char * const excp_names[] = { [EXCCODE_IPE] = "Instruction privilege error", [EXCCODE_FPE] = "Floating Point Exception", [EXCCODE_DBP] = "Debug breakpoint", + [EXCCODE_BCE] = "Bound Check Exception", }; const char *loongarch_exception_name(int32_t exception) @@ -82,6 +82,7 @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) env->pc = value; } +#ifndef CONFIG_USER_ONLY #include "hw/loongarch/virt.h" void loongarch_cpu_set_irq(void *opaque, int irq, int level) @@ -139,7 +140,7 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) if (cs->exception_index != EXCCODE_INT) { if (cs->exception_index < 0 || - cs->exception_index > ARRAY_SIZE(excp_names)) { + cs->exception_index >= ARRAY_SIZE(excp_names)) { name = "unknown"; } else { name = excp_names[cs->exception_index]; @@ -171,23 +172,26 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) cause = cs->exception_index; update_badinstr = 0; break; - case EXCCODE_ADEM: case EXCCODE_SYS: case EXCCODE_BRK: + case EXCCODE_INE: + case EXCCODE_IPE: + case EXCCODE_FPE: + case EXCCODE_BCE: + env->CSR_BADV = env->pc; + QEMU_FALLTHROUGH; + case EXCCODE_ADEM: case EXCCODE_PIL: case EXCCODE_PIS: case EXCCODE_PME: case EXCCODE_PNR: case EXCCODE_PNX: case EXCCODE_PPI: - case EXCCODE_INE: - case EXCCODE_IPE: - case EXCCODE_FPE: cause = cs->exception_index; break; default: - qemu_log("Error: exception(%d) '%s' has not been supported\n", - cs->exception_index, excp_names[cs->exception_index]); + qemu_log("Error: exception(%d) has not been supported\n", + cs->exception_index); abort(); } @@ -219,6 +223,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); + if (vec_size) { + vec_size = (1 << vec_size) * 4; + } + if (cs->exception_index == EXCCODE_INT) { /* Interrupt */ uint32_t vector = 0; @@ -292,6 +300,7 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } +#endif #ifdef CONFIG_TCG static void loongarch_cpu_synchronize_from_tb(CPUState *cs, @@ -306,6 +315,9 @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs, 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; @@ -316,6 +328,7 @@ static bool loongarch_cpu_has_work(CPUState *cs) } return has_work; +#endif } static void loongarch_la464_initfn(Object *obj) @@ -328,6 +341,7 @@ static void loongarch_la464_initfn(Object *obj) env->cpucfg[i] = 0x0; } + cpu->dtb_compatible = "loongarch,Loongson-3A5000"; env->cpucfg[0] = 0x14c010; /* PRID */ uint32_t data = 0; @@ -393,7 +407,7 @@ static void loongarch_la464_initfn(Object *obj) data = 0; data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15); data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14); - data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6); + data = FIELD_DP32(data, CPUCFG20, L3IU_SIZE, 6); env->cpucfg[20] = data; env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); @@ -464,7 +478,10 @@ static void loongarch_cpu_reset(DeviceState *dev) env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0); } +#ifndef CONFIG_USER_ONLY env->pc = 0x1c000000; + memset(env->tlb, 0, sizeof(env->tlb)); +#endif restore_fp_status(env); cs->exception_index = -1; @@ -495,6 +512,7 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) lacc->parent_realize(dev, errp); } +#ifndef CONFIG_USER_ONLY static void loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -529,13 +547,16 @@ static const MemoryRegionOps loongarch_qemu_ops = { .max_access_size = 8, }, }; +#endif static void loongarch_cpu_init(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); - CPULoongArchState *env = &cpu->env; cpu_set_cpustate_pointers(cpu); + +#ifndef CONFIG_USER_ONLY + CPULoongArchState *env = &cpu->env; qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS); timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL, &loongarch_constant_timer_cb, cpu); @@ -545,17 +566,28 @@ static void loongarch_cpu_init(Object *obj) 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 } static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; - char *typename; - typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); - oc = object_class_by_name(typename); - g_free(typename); - return oc; + oc = object_class_by_name(cpu_model); + if (!oc) { + g_autofree char *typename + = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); + oc = object_class_by_name(typename); + if (!oc) { + return NULL; + } + } + + if (object_class_dynamic_cast(oc, TYPE_LOONGARCH_CPU) + && !object_class_is_abstract(oc)) { + return oc; + } + return NULL; } void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) @@ -612,18 +644,22 @@ static struct TCGCPUOps loongarch_tcg_ops = { .initialize = loongarch_translate_init, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, +#ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, .do_interrupt = loongarch_cpu_do_interrupt, .do_transaction_failed = loongarch_cpu_do_transaction_failed, +#endif }; #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps loongarch_sysemu_ops = { .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, }; +#endif static void loongarch_cpu_class_init(ObjectClass *c, void *data) { @@ -639,8 +675,10 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) cc->has_work = loongarch_cpu_has_work; cc->dump_state = loongarch_cpu_dump_state; cc->set_pc = loongarch_cpu_set_pc; +#ifndef CONFIG_USER_ONLY dc->vmsd = &vmstate_loongarch_cpu; cc->sysemu_ops = &loongarch_sysemu_ops; +#endif cc->disas_set_info = loongarch_cpu_disas_set_info; cc->gdb_read_register = loongarch_cpu_gdb_read_register; cc->gdb_write_register = loongarch_cpu_gdb_write_register; diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 71a5036c3c6..a36349df83e 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -246,8 +246,6 @@ typedef struct CPUArchState { uint64_t lladdr; /* LL virtual address compared against SC */ uint64_t llval; - uint64_t badaddr; - /* LoongArch CSRs */ uint64_t CSR_CRMD; uint64_t CSR_PRMD; @@ -303,6 +301,7 @@ typedef struct CPUArchState { uint64_t CSR_DERA; uint64_t CSR_DSAVE; +#ifndef CONFIG_USER_ONLY LoongArchTLB tlb[LOONGARCH_TLB_MAX]; AddressSpace address_space_iocsr; @@ -310,6 +309,7 @@ typedef struct CPUArchState { MemoryRegion iocsr_mem; bool load_elf; uint64_t elf_address; +#endif } CPULoongArchState; /** @@ -326,6 +326,9 @@ struct ArchCPU { CPUNegativeOffsetState neg; CPULoongArchState env; QEMUTimer timer; + + /* 'compatible' string for this CPU for Linux device trees */ + const char *dtb_compatible; }; #define TYPE_LOONGARCH_CPU "loongarch-cpu" @@ -360,12 +363,16 @@ struct LoongArchCPUClass { static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch) { +#ifdef CONFIG_USER_ONLY + return MMU_USER_IDX; +#else uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); if (!pg) { return MMU_DA_IDX; } return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); +#endif } static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c index 24a9389364b..7e027878957 100644 --- a/target/loongarch/csr_helper.c +++ b/target/loongarch/csr_helper.c @@ -81,7 +81,9 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) 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/fpu_helper.c b/target/loongarch/fpu_helper.c index 3d0cb8dd0d2..bd76529219a 100644 --- a/target/loongarch/fpu_helper.c +++ b/target/loongarch/fpu_helper.c @@ -13,9 +13,6 @@ #include "fpu/softfloat.h" #include "internals.h" -#define FLOAT_TO_INT32_OVERFLOW 0x7fffffff -#define FLOAT_TO_INT64_OVERFLOW 0x7fffffffffffffffULL - static inline uint64_t nanbox_s(float32 fp) { return fp | MAKE_64BIT_MASK(32, 32); @@ -544,9 +541,10 @@ uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -561,9 +559,10 @@ uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -578,9 +577,10 @@ uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -595,9 +595,10 @@ uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -612,9 +613,10 @@ uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -629,9 +631,10 @@ uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -646,9 +649,10 @@ uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -663,9 +667,10 @@ uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -679,9 +684,10 @@ uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64_round_to_zero(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -695,9 +701,10 @@ uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64_round_to_zero((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -711,9 +718,10 @@ uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32_round_to_zero(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -727,9 +735,10 @@ uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int32_round_to_zero((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return (uint64_t)fd; @@ -744,9 +753,10 @@ uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -761,9 +771,10 @@ uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -778,9 +789,10 @@ uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -795,9 +807,10 @@ uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int32((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return (uint64_t)fd; @@ -808,9 +821,10 @@ uint64_t helper_ftint_l_d(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = float64_to_int64(fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -821,9 +835,10 @@ uint64_t helper_ftint_l_s(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = float32_to_int64((uint32_t)fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -834,9 +849,10 @@ uint64_t helper_ftint_w_s(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) - & (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -847,9 +863,10 @@ uint64_t helper_ftint_w_d(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = (uint64_t)float64_to_int32(fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) - & (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index 0c488342011..24e126fb2d7 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -21,7 +21,7 @@ int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) } else if (n == 32) { return gdb_get_regl(mem_buf, env->pc); } else if (n == 33) { - return gdb_get_regl(mem_buf, env->badaddr); + return gdb_get_regl(mem_buf, env->CSR_BADV); } return 0; } diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h index 85c11a60d4a..cbbe008f32f 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -95,6 +95,7 @@ DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_1(rdtime_d, i64, env) +#ifndef CONFIG_USER_ONLY /* CSRs helper */ DEF_HELPER_1(csrrd_pgd, i64, env) DEF_HELPER_1(csrrd_tval, i64, env) @@ -128,3 +129,4 @@ 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 diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc index 53596c4f77c..9c4dcbfcfb9 100644 --- a/target/loongarch/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/insn_trans/trans_privileged.c.inc @@ -7,6 +7,41 @@ #include "cpu-csr.h" +#ifdef CONFIG_USER_ONLY + +#define GEN_FALSE_TRANS(name) \ +static bool trans_##name(DisasContext *ctx, arg_##name * a) \ +{ \ + return false; \ +} + +GEN_FALSE_TRANS(csrrd) +GEN_FALSE_TRANS(csrwr) +GEN_FALSE_TRANS(csrxchg) +GEN_FALSE_TRANS(iocsrrd_b) +GEN_FALSE_TRANS(iocsrrd_h) +GEN_FALSE_TRANS(iocsrrd_w) +GEN_FALSE_TRANS(iocsrrd_d) +GEN_FALSE_TRANS(iocsrwr_b) +GEN_FALSE_TRANS(iocsrwr_h) +GEN_FALSE_TRANS(iocsrwr_w) +GEN_FALSE_TRANS(iocsrwr_d) +GEN_FALSE_TRANS(tlbsrch) +GEN_FALSE_TRANS(tlbrd) +GEN_FALSE_TRANS(tlbwr) +GEN_FALSE_TRANS(tlbfill) +GEN_FALSE_TRANS(tlbclr) +GEN_FALSE_TRANS(tlbflush) +GEN_FALSE_TRANS(invtlb) +GEN_FALSE_TRANS(cacop) +GEN_FALSE_TRANS(ldpte) +GEN_FALSE_TRANS(lddir) +GEN_FALSE_TRANS(ertn) +GEN_FALSE_TRANS(dbcl) +GEN_FALSE_TRANS(idle) + +#else + typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); @@ -464,3 +499,4 @@ static bool trans_idle(DisasContext *ctx, arg_idle *a) ctx->base.is_jmp = DISAS_NORETURN; return true; } +#endif diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 9d50fbdd813..ea227362b66 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -33,6 +33,7 @@ const char *loongarch_exception_name(int32_t exception); void restore_fp_status(CPULoongArchState *env); +#ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -48,6 +49,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool probe, uintptr_t retaddr); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +#endif /* !CONFIG_USER_ONLY */ int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n); int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n); diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c index d87049851f4..568c0716016 100644 --- a/target/loongarch/op_helper.c +++ b/target/loongarch/op_helper.c @@ -49,14 +49,14 @@ target_ulong helper_bitswap(target_ulong v) void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) { if (rj > rk) { - do_raise_exception(env, EXCCODE_ADEM, GETPC()); + do_raise_exception(env, EXCCODE_BCE, 0); } } void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) { if (rj <= rk) { - do_raise_exception(env, EXCCODE_ADEM, GETPC()); + do_raise_exception(env, EXCCODE_BCE, 0); } } @@ -81,11 +81,14 @@ target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz) target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj) { - return rj > 21 ? 0 : env->cpucfg[rj]; + return rj >= ARRAY_SIZE(env->cpucfg) ? 0 : env->cpucfg[rj]; } uint64_t helper_rdtime_d(CPULoongArchState *env) { +#ifdef CONFIG_USER_ONLY + return cpu_get_host_ticks(); +#else uint64_t plv; LoongArchCPU *cpu = env_archcpu(env); @@ -95,8 +98,10 @@ uint64_t helper_rdtime_d(CPULoongArchState *env) } return cpu_loongarch_get_constant_timer_counter(cpu); +#endif } +#ifndef CONFIG_USER_ONLY void helper_ertn(CPULoongArchState *env) { uint64_t csr_pplv, csr_pie; @@ -131,3 +136,4 @@ void helper_idle(CPULoongArchState *env) cs->halted = 1; do_raise_exception(env, EXCP_HLT, 0); } +#endif diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c index bab19c7e050..610b6d123c5 100644 --- a/target/loongarch/tlb_helper.c +++ b/target/loongarch/tlb_helper.c @@ -298,7 +298,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) } else { tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); } - pagesize = 1 << tlb_ps; + pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); if (tlb_v0) { @@ -736,7 +736,7 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT))); ps = ptbase + ptwidth - 1; if (odd) { - tmp0 += (1 << ps); + tmp0 += MAKE_64BIT_MASK(ps, 1); } } else { /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 37343d47e24..d0697ddbd10 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -21,13 +21,8 @@ #include "cpu.h" #include "exec/gdbstub.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024) -#else -#include "exec/softmmu-semi.h" +#include "semihosting/softmmu-uaccess.h" #include "hw/boards.h" -#endif #include "qemu/log.h" #define HOSTED_EXIT 0 @@ -45,38 +40,6 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct m68k_gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - -#define GDB_O_RDONLY 0x0 -#define GDB_O_WRONLY 0x1 -#define GDB_O_RDWR 0x2 -#define GDB_O_APPEND 0x8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - static int translate_openflags(int flags) { int hf; @@ -98,11 +61,13 @@ static int translate_openflags(int flags) static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) { - struct m68k_gdb_stat *p; + struct gdb_stat *p; - if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0))) + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); + if (!p) { /* FIXME - should this return an error code? */ return; + } p->gdb_st_dev = cpu_to_be32(s->st_dev); p->gdb_st_ino = cpu_to_be32(s->st_ino); p->gdb_st_mode = cpu_to_be32(s->st_mode); @@ -122,11 +87,14 @@ static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) p->gdb_st_atime = cpu_to_be32(s->st_atime); p->gdb_st_mtime = cpu_to_be32(s->st_mtime); p->gdb_st_ctime = cpu_to_be32(s->st_ctime); - unlock_user(p, addr, sizeof(struct m68k_gdb_stat)); + unlock_user(p, addr, sizeof(struct gdb_stat)); } -static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) +static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + target_ulong args = env->dregs[1]; if (put_user_u32(ret, args) || put_user_u32(err, args + 4)) { @@ -140,8 +108,11 @@ static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) } } -static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err) +static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + target_ulong args = env->dregs[1]; if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || @@ -152,25 +123,6 @@ static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err) } } -static int m68k_semi_is_fseek; - -static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - if (m68k_semi_is_fseek) { - /* - * FIXME: We've already lost the high bits of the fseek - * return value. - */ - m68k_semi_return_u64(env, ret, err); - m68k_semi_is_fseek = 0; - } else { - m68k_semi_return_u32(env, ret, err); - } -} - /* * Read the input value from the argument block; fail the semihosting * call if the memory read fails. @@ -185,6 +137,7 @@ static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) void do_m68k_semihosting(CPUM68KState *env, int nr) { + CPUState *cs = env_cpu(env); uint32_t args; target_ulong arg0, arg1, arg2, arg3; void *p; @@ -203,7 +156,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, + gdb_do_syscall(m68k_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1, arg2, arg3); return; } else { @@ -224,7 +177,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) int fd = arg0; if (fd > 2) { if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "close,%x", arg0); + gdb_do_syscall(m68k_semi_u32_cb, "close,%x", arg0); return; } else { result = close(fd); @@ -240,7 +193,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "read,%x,%x,%x", arg0, arg1, len); return; } else { @@ -260,7 +213,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "write,%x,%x,%x", arg0, arg1, len); return; } else { @@ -283,12 +236,11 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(3); off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); if (use_gdb_syscalls()) { - m68k_semi_is_fseek = 1; - gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x", + gdb_do_syscall(m68k_semi_u64_cb, "fseek,%x,%lx,%x", arg0, off, arg3); } else { off = lseek(arg0, off, arg3); - m68k_semi_return_u64(env, off, errno); + m68k_semi_u64_cb(cs, off, errno); } return; } @@ -298,7 +250,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "rename,%s,%s", + gdb_do_syscall(m68k_semi_u32_cb, "rename,%s,%s", arg0, (int)arg1, arg2, (int)arg3); return; } else { @@ -318,7 +270,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "unlink,%s", + gdb_do_syscall(m68k_semi_u32_cb, "unlink,%s", arg0, (int)arg1); return; } else { @@ -337,7 +289,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(1); GET_ARG(2); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "stat,%s,%x", + gdb_do_syscall(m68k_semi_u32_cb, "stat,%s,%x", arg0, (int)arg1, arg2); return; } else { @@ -359,7 +311,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "fstat,%x,%x", arg0, arg1); return; } else { @@ -374,7 +326,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "gettimeofday,%x,%x", arg0, arg1); return; } else { @@ -395,7 +347,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) case HOSTED_ISATTY: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "isatty,%x", arg0); + gdb_do_syscall(m68k_semi_u32_cb, "isatty,%x", arg0); return; } else { result = isatty(arg0); @@ -405,7 +357,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "system,%s", + gdb_do_syscall(m68k_semi_u32_cb, "system,%s", arg0, (int)arg1); return; } else { @@ -420,48 +372,17 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) } break; case HOSTED_INIT_SIM: -#if defined(CONFIG_USER_ONLY) - { - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - /* Allocate the heap using sbrk. */ - if (!ts->heap_limit) { - abi_ulong ret; - uint32_t size; - uint32_t base; - - base = do_brk(0); - size = SEMIHOSTING_HEAP_SIZE; - /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(base + size); - if (ret >= (base + size)) { - break; - } - size >>= 1; - } - ts->heap_limit = base + size; - } - /* - * This call may happen before we have writable memory, so return - * values directly in registers. - */ - env->dregs[1] = ts->heap_limit; - env->aregs[7] = ts->stack_base; - } -#else /* * FIXME: This is wrong for boards where RAM does not start at * address zero. */ env->dregs[1] = current_machine->ram_size; env->aregs[7] = current_machine->ram_size; -#endif return; default: cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); result = 0; } failed: - m68k_semi_return_u32(env, result, errno); + m68k_semi_u32_cb(cs, result, errno); } diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 05cd9fbd1e8..27d2d7ba874 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -4,14 +4,16 @@ m68k_ss.add(files( 'fpu_helper.c', 'gdbstub.c', 'helper.c', - 'm68k-semi.c', 'op_helper.c', 'softfloat.c', 'translate.c', )) m68k_softmmu_ss = ss.source_set() -m68k_softmmu_ss.add(files('monitor.c')) +m68k_softmmu_ss.add(files( + 'm68k-semi.c', + 'monitor.c' +)) target_arch += {'m68k': m68k_ss} target_softmmu_arch += {'m68k': m68k_softmmu_ss} diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index 582f9400702..7f53c94ec85 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -921,6 +921,34 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSP_R2, .mmu_type = MMU_TYPE_R4000, }, + { + /* + * Octeon 68xx with MIPS64 Cavium Octeon features. + */ + .name = "Octeon68XX", + .CP0_PRid = 0x000D9100, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (0x3F << CP0C1_MMU) | + (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | + (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA) | (1 << CP0C3_DSPP) , + .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | + (0x3c << CP0C4_KScrExist) | (1U << CP0C4_MMUExtDef) | + (3U << CP0C4_MMUSizeExt), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .CP0_PageGrain = (1 << CP0PG_ELPA), + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x12F8FFFF, + .SEGBITS = 42, + .PABITS = 49, + .insn_flags = CPU_MIPS64R2 | INSN_OCTEON | ASE_DSP, + .mmu_type = MMU_TYPE_R4000, + }, #endif }; diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 42efa989e40..0a085643a3f 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1252,8 +1252,9 @@ enum { EXCP_MSAFPE, EXCP_TLBXI, EXCP_TLBRI, + EXCP_SEMIHOST, - EXCP_LAST = EXCP_TLBRI, + EXCP_LAST = EXCP_SEMIHOST, }; /* diff --git a/target/mips/mips-defs.h b/target/mips/mips-defs.h index 0a12d982a72..a6cebe0265c 100644 --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -42,6 +42,7 @@ #define INSN_LOONGSON2E 0x0000040000000000ULL #define INSN_LOONGSON2F 0x0000080000000000ULL #define INSN_LOONGSON3A 0x0000100000000000ULL +#define INSN_OCTEON 0x0000200000000000ULL /* * bits 52-63: vendor-specific ASEs */ diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index 0b21e0872bd..2bd77a61de6 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -125,6 +125,7 @@ static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_TLBRI] = "TLB read-inhibit", [EXCP_MSADIS] = "MSA disabled", [EXCP_MSAFPE] = "MSA floating point", + [EXCP_SEMIHOST] = "Semihosting", }; const char *mips_exception_name(int32_t exception) diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index 98003779ae8..7ee969ec8f2 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -3,6 +3,7 @@ gen = [ decodetree.process('msa.decode', extra_args: '--decode=decode_ase_msa'), decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'), decodetree.process('vr54xx.decode', extra_args: '--decode=decode_ext_vr54xx'), + decodetree.process('octeon.decode', extra_args: '--decode=decode_ext_octeon'), ] mips_ss.add(gen) @@ -24,6 +25,7 @@ mips_ss.add(files( )) mips_ss.add(when: 'TARGET_MIPS64', if_true: files( 'tx79_translate.c', + 'octeon_translate.c', ), if_false: files( 'mxu_translate.c', )) diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index fc6ede75b80..274caf2c3c4 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -826,7 +826,7 @@ static void gen_pool16c_insn(DisasContext *ctx) break; case SDBBP16: if (is_uhi(extract32(ctx->opcode, 0, 4))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { /* * XXX: not clear which exception should be raised @@ -942,7 +942,7 @@ static void gen_pool16c_r6_insn(DisasContext *ctx) case R6_SDBBP16: /* SDBBP16 */ if (is_uhi(extract32(ctx->opcode, 6, 4))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { generate_exception(ctx, EXCP_RI); @@ -1311,7 +1311,7 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) break; case SDBBP: if (is_uhi(extract32(ctx->opcode, 16, 10))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { check_insn(ctx, ISA_MIPS_R1); if (ctx->hflags & MIPS_HFLAG_SBRI) { diff --git a/target/mips/tcg/mips16e_translate.c.inc b/target/mips/tcg/mips16e_translate.c.inc index f57e0a5f2a9..0a3ba252e4d 100644 --- a/target/mips/tcg/mips16e_translate.c.inc +++ b/target/mips/tcg/mips16e_translate.c.inc @@ -952,7 +952,7 @@ static int decode_ase_mips16e(CPUMIPSState *env, DisasContext *ctx) break; case RR_SDBBP: if (is_uhi(extract32(ctx->opcode, 5, 6))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { /* * XXX: not clear which exception should be raised diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index c0ba2bf1b19..ecb0ebed570 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -3695,7 +3695,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) break; case NM_SDBBP: if (is_uhi(extract32(ctx->opcode, 0, 19))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -4634,7 +4634,7 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) break; case NM_SDBBP16: if (is_uhi(extract32(ctx->opcode, 0, 3))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); diff --git a/target/mips/tcg/octeon.decode b/target/mips/tcg/octeon.decode new file mode 100644 index 00000000000..8929ad088e9 --- /dev/null +++ b/target/mips/tcg/octeon.decode @@ -0,0 +1,41 @@ +# Octeon Architecture Module instruction set +# +# Copyright (C) 2022 Pavel Dovgalyuk +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# + +# Branch on bit set or clear +# BBIT0 110010 ..... ..... ................ +# BBIT032 110110 ..... ..... ................ +# BBIT1 111010 ..... ..... ................ +# BBIT132 111110 ..... ..... ................ + +%bbit_p 28:1 16:5 +BBIT 11 set:1 . 10 rs:5 ..... offset:16 p=%bbit_p + +# Arithmetic +# BADDU rd, rs, rt +# DMUL rd, rs, rt +# EXTS rt, rs, p, lenm1 +# EXTS32 rt, rs, p, lenm1 +# CINS rt, rs, p, lenm1 +# CINS32 rt, rs, p, lenm1 +# DPOP rd, rs +# POP rd, rs +# SEQ rd, rs, rt +# SEQI rt, rs, immediate +# SNE rd, rs, rt +# SNEI rt, rs, immediate + +@r3 ...... rs:5 rt:5 rd:5 ..... ...... +%bitfield_p 0:1 6:5 +@bitfield ...... rs:5 rt:5 lenm1:5 ..... ..... . p=%bitfield_p + +BADDU 011100 ..... ..... ..... 00000 101000 @r3 +DMUL 011100 ..... ..... ..... 00000 000011 @r3 +EXTS 011100 ..... ..... ..... ..... 11101 . @bitfield +CINS 011100 ..... ..... ..... ..... 11001 . @bitfield +POP 011100 rs:5 00000 rd:5 00000 10110 dw:1 +SEQNE 011100 rs:5 rt:5 rd:5 00000 10101 ne:1 +SEQNEI 011100 rs:5 rt:5 imm:s10 10111 ne:1 diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c new file mode 100644 index 00000000000..6a207d2e7ed --- /dev/null +++ b/target/mips/tcg/octeon_translate.c @@ -0,0 +1,201 @@ +/* + * Octeon-specific instructions translation routines + * + * Copyright (c) 2022 Pavel Dovgalyuk + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "exec/helper-gen.h" +#include "translate.h" + +/* Include the auto-generated decoder. */ +#include "decode-octeon.c.inc" + +static bool trans_BBIT(DisasContext *ctx, arg_BBIT *a) +{ + TCGv p; + + if (ctx->hflags & MIPS_HFLAG_BMASK) { + LOG_DISAS("Branch in delay / forbidden slot at PC 0x" + TARGET_FMT_lx "\n", ctx->base.pc_next); + generate_exception_end(ctx, EXCP_RI); + return true; + } + + /* Load needed operands */ + TCGv t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + + p = tcg_constant_tl(1ULL << a->p); + if (a->set) { + tcg_gen_and_tl(bcond, p, t0); + } else { + tcg_gen_andc_tl(bcond, p, t0); + } + + ctx->hflags |= MIPS_HFLAG_BC; + ctx->btarget = ctx->base.pc_next + 4 + a->offset * 4; + ctx->hflags |= MIPS_HFLAG_BDS32; + + tcg_temp_free(t0); + return true; +} + +static bool trans_BADDU(DisasContext *ctx, arg_BADDU *a) +{ + TCGv t0, t1; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_andi_i64(cpu_gpr[a->rd], t0, 0xff); + + tcg_temp_free(t0); + tcg_temp_free(t1); + + return true; +} + +static bool trans_DMUL(DisasContext *ctx, arg_DMUL *a) +{ + TCGv t0, t1; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + tcg_gen_mul_i64(cpu_gpr[a->rd], t0, t1); + + tcg_temp_free(t0); + tcg_temp_free(t1); + + return true; +} + +static bool trans_EXTS(DisasContext *ctx, arg_EXTS *a) +{ + TCGv t0; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + tcg_gen_sextract_tl(t0, t0, a->p, a->lenm1 + 1); + gen_store_gpr(t0, a->rt); + tcg_temp_free(t0); + + return true; +} + +static bool trans_CINS(DisasContext *ctx, arg_CINS *a) +{ + TCGv t0; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + tcg_gen_deposit_z_tl(t0, t0, a->p, a->lenm1 + 1); + gen_store_gpr(t0, a->rt); + tcg_temp_free(t0); + + return true; +} + +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + TCGv t0; + + if (a->rd == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + if (!a->dw) { + tcg_gen_andi_i64(t0, t0, 0xffffffff); + } + tcg_gen_ctpop_tl(t0, t0); + gen_store_gpr(t0, a->rd); + tcg_temp_free(t0); + + return true; +} + +static bool trans_SEQNE(DisasContext *ctx, arg_SEQNE *a) +{ + TCGv t0, t1; + + if (a->rd == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + if (a->ne) { + tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr[a->rd], t1, t0); + } else { + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr[a->rd], t1, t0); + } + + tcg_temp_free(t0); + tcg_temp_free(t1); + + return true; +} + +static bool trans_SEQNEI(DisasContext *ctx, arg_SEQNEI *a) +{ + TCGv t0; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + + gen_load_gpr(t0, a->rs); + + /* Sign-extend to 64 bit value */ + target_ulong imm = a->imm; + if (a->ne) { + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr[a->rt], t0, imm); + } else { + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], t0, imm); + } + + tcg_temp_free(t0); + + return true; +} diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index b4a383ae90c..5fb1ad90920 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -20,10 +20,12 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" -#include "exec/helper-proto.h" -#include "exec/softmmu-semi.h" +#include "exec/gdbstub.h" +#include "semihosting/softmmu-uaccess.h" #include "semihosting/semihost.h" #include "semihosting/console.h" +#include "semihosting/syscalls.h" +#include "internal.h" typedef enum UHIOp { UHI_exit = 1, @@ -74,293 +76,281 @@ enum UHIOpenFlags { UHIOpen_EXCL = 0x800 }; -static int errno_mips(int host_errno) +enum UHIErrno { + UHI_EACCESS = 13, + UHI_EAGAIN = 11, + UHI_EBADF = 9, + UHI_EBADMSG = 77, + UHI_EBUSY = 16, + UHI_ECONNRESET = 104, + UHI_EEXIST = 17, + UHI_EFBIG = 27, + UHI_EINTR = 4, + UHI_EINVAL = 22, + UHI_EIO = 5, + UHI_EISDIR = 21, + UHI_ELOOP = 92, + UHI_EMFILE = 24, + UHI_EMLINK = 31, + UHI_ENAMETOOLONG = 91, + UHI_ENETDOWN = 115, + UHI_ENETUNREACH = 114, + UHI_ENFILE = 23, + UHI_ENOBUFS = 105, + UHI_ENOENT = 2, + UHI_ENOMEM = 12, + UHI_ENOSPC = 28, + UHI_ENOSR = 63, + UHI_ENOTCONN = 128, + UHI_ENOTDIR = 20, + UHI_ENXIO = 6, + UHI_EOVERFLOW = 139, + UHI_EPERM = 1, + UHI_EPIPE = 32, + UHI_ERANGE = 34, + UHI_EROFS = 30, + UHI_ESPIPE = 29, + UHI_ETIMEDOUT = 116, + UHI_ETXTBSY = 26, + UHI_EWOULDBLOCK = 11, + UHI_EXDEV = 18, +}; + +static void report_fault(CPUMIPSState *env) { - /* Errno values taken from asm-mips/errno.h */ - switch (host_errno) { - case 0: return 0; - case ENAMETOOLONG: return 78; -#ifdef EOVERFLOW - case EOVERFLOW: return 79; -#endif -#ifdef ELOOP - case ELOOP: return 90; -#endif - default: return EINVAL; - } + int op = env->active_tc.gpr[25]; + error_report("Fault during UHI operation %d", op); + abort(); } -static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, - target_ulong vaddr) +static void uhi_cb(CPUState *cs, uint64_t ret, int err) { - hwaddr len = sizeof(struct UHIStat); - UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); - if (!dst) { - errno = EFAULT; - return -1; - } + CPUMIPSState *env = cs->env_ptr; - dst->uhi_st_dev = tswap16(src->st_dev); - dst->uhi_st_ino = tswap16(src->st_ino); - dst->uhi_st_mode = tswap32(src->st_mode); - dst->uhi_st_nlink = tswap16(src->st_nlink); - dst->uhi_st_uid = tswap16(src->st_uid); - dst->uhi_st_gid = tswap16(src->st_gid); - dst->uhi_st_rdev = tswap16(src->st_rdev); - dst->uhi_st_size = tswap64(src->st_size); - dst->uhi_st_atime = tswap64(src->st_atime); - dst->uhi_st_mtime = tswap64(src->st_mtime); - dst->uhi_st_ctime = tswap64(src->st_ctime); -#ifdef _WIN32 - dst->uhi_st_blksize = 0; - dst->uhi_st_blocks = 0; -#else - dst->uhi_st_blksize = tswap64(src->st_blksize); - dst->uhi_st_blocks = tswap64(src->st_blocks); -#endif - unlock_user(dst, vaddr, len); - return 0; -} +#define E(N) case E##N: err = UHI_E##N; break -static int get_open_flags(target_ulong target_flags) -{ - int open_flags = 0; - - if (target_flags & UHIOpen_RDWR) { - open_flags |= O_RDWR; - } else if (target_flags & UHIOpen_WRONLY) { - open_flags |= O_WRONLY; - } else { - open_flags |= O_RDONLY; + switch (err) { + case 0: + break; + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(BUSY); + E(EXIST); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + err = UHI_EINVAL; + break; + case EFAULT: + report_fault(env); } - open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; - open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0; - open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0; - open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0; +#undef E - return open_flags; + env->active_tc.gpr[2] = ret; + env->active_tc.gpr[3] = err; } -static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, - target_ulong len, target_ulong offset) +static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) { - int num_of_bytes; - void *dst = lock_user(VERIFY_READ, vaddr, len, 1); - if (!dst) { - errno = EFAULT; - return -1; - } + QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat)); - if (offset) { -#ifdef _WIN32 - num_of_bytes = 0; -#else - num_of_bytes = pwrite(fd, dst, len, offset); -#endif - } else { - num_of_bytes = write(fd, dst, len); - } + if (!err) { + CPUMIPSState *env = cs->env_ptr; + target_ulong addr = env->active_tc.gpr[5]; + UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1); + struct gdb_stat s; - unlock_user(dst, vaddr, 0); - return num_of_bytes; -} + if (!dst) { + report_fault(env); + } -static int read_from_file(CPUMIPSState *env, target_ulong fd, - target_ulong vaddr, target_ulong len, - target_ulong offset) -{ - int num_of_bytes; - void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); - if (!dst) { - errno = EFAULT; - return -1; - } + memcpy(&s, dst, sizeof(struct gdb_stat)); + memset(dst, 0, sizeof(UHIStat)); - if (offset) { -#ifdef _WIN32 - num_of_bytes = 0; -#else - num_of_bytes = pread(fd, dst, len, offset); -#endif - } else { - num_of_bytes = read(fd, dst, len); - } + dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev)); + dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino)); + dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode)); + dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink)); + dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid)); + dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid)); + dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev)); + dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size)); + dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime)); + dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime)); + dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime)); + dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize)); + dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks)); - unlock_user(dst, vaddr, len); - return num_of_bytes; -} - -static int copy_argn_to_target(CPUMIPSState *env, int arg_num, - target_ulong vaddr) -{ - int strsize = strlen(semihosting_get_arg(arg_num)) + 1; - char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); - if (!dst) { - return -1; + unlock_user(dst, addr, sizeof(UHIStat)); } - strcpy(dst, semihosting_get_arg(arg_num)); - - unlock_user(dst, vaddr, strsize); - return 0; + uhi_cb(cs, ret, err); } -#define GET_TARGET_STRING(p, addr) \ - do { \ - p = lock_user_string(addr); \ - if (!p) { \ - gpr[2] = -1; \ - gpr[3] = EFAULT; \ - return; \ - } \ - } while (0) - -#define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \ - do { \ - p = lock_user_string(addr); \ - if (!p) { \ - gpr[2] = -1; \ - gpr[3] = EFAULT; \ - return; \ - } \ - p2 = lock_user_string(addr2); \ - if (!p2) { \ - unlock_user(p, addr, 0); \ - gpr[2] = -1; \ - gpr[3] = EFAULT; \ - return; \ - } \ - } while (0) - -#define FREE_TARGET_STRING(p, gpr) \ - do { \ - unlock_user(p, gpr, 0); \ - } while (0) - -void helper_do_semihosting(CPUMIPSState *env) +void mips_semihosting(CPUMIPSState *env) { + CPUState *cs = env_cpu(env); target_ulong *gpr = env->active_tc.gpr; const UHIOp op = gpr[25]; - char *p, *p2; + char *p; switch (op) { case UHI_exit: - qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); + gdb_exit(gpr[4]); exit(gpr[4]); + case UHI_open: - GET_TARGET_STRING(p, gpr[4]); - if (!strcmp("/dev/stdin", p)) { - gpr[2] = 0; - } else if (!strcmp("/dev/stdout", p)) { - gpr[2] = 1; - } else if (!strcmp("/dev/stderr", p)) { - gpr[2] = 2; - } else { - gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); - gpr[3] = errno_mips(errno); + { + target_ulong fname = gpr[4]; + int ret = -1; + + p = lock_user_string(fname); + if (!p) { + report_fault(env); + } + if (!strcmp("/dev/stdin", p)) { + ret = 0; + } else if (!strcmp("/dev/stdout", p)) { + ret = 1; + } else if (!strcmp("/dev/stderr", p)) { + ret = 2; + } + unlock_user(p, fname, 0); + + /* FIXME: reusing a guest fd doesn't seem correct. */ + if (ret >= 0) { + gpr[2] = ret; + break; + } + + semihost_sys_open(cs, uhi_cb, fname, 0, gpr[5], gpr[6]); } - FREE_TARGET_STRING(p, gpr[4]); break; + case UHI_close: - if (gpr[4] < 3) { - /* ignore closing stdin/stdout/stderr */ - gpr[2] = 0; - return; - } - gpr[2] = close(gpr[4]); - gpr[3] = errno_mips(errno); + semihost_sys_close(cs, uhi_cb, gpr[4]); break; case UHI_read: - gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); - gpr[3] = errno_mips(errno); + semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); break; case UHI_write: - gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); - gpr[3] = errno_mips(errno); + semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); break; case UHI_lseek: - gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); - gpr[3] = errno_mips(errno); + semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); break; case UHI_unlink: - GET_TARGET_STRING(p, gpr[4]); - gpr[2] = remove(p); - gpr[3] = errno_mips(errno); - FREE_TARGET_STRING(p, gpr[4]); + semihost_sys_remove(cs, uhi_cb, gpr[4], 0); break; case UHI_fstat: - { - struct stat sbuf; - memset(&sbuf, 0, sizeof(sbuf)); - gpr[2] = fstat(gpr[4], &sbuf); - gpr[3] = errno_mips(errno); - if (gpr[2]) { - return; - } - gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); - gpr[3] = errno_mips(errno); - } + semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]); break; + case UHI_argc: gpr[2] = semihosting_get_argc(); break; case UHI_argnlen: - if (gpr[4] >= semihosting_get_argc()) { - gpr[2] = -1; - return; + { + const char *s = semihosting_get_arg(gpr[4]); + gpr[2] = s ? strlen(s) : -1; } - gpr[2] = strlen(semihosting_get_arg(gpr[4])); break; case UHI_argn: - if (gpr[4] >= semihosting_get_argc()) { - gpr[2] = -1; - return; + { + const char *s = semihosting_get_arg(gpr[4]); + target_ulong addr; + size_t len; + + if (!s) { + gpr[2] = -1; + break; + } + len = strlen(s) + 1; + addr = gpr[5]; + p = lock_user(VERIFY_WRITE, addr, len, 0); + if (!p) { + report_fault(env); + } + memcpy(p, s, len); + unlock_user(p, addr, len); + gpr[2] = 0; } - gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); break; + case UHI_plog: - GET_TARGET_STRING(p, gpr[4]); - p2 = strstr(p, "%d"); - if (p2) { - int char_num = p2 - p; - GString *s = g_string_new_len(p, char_num); - g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2); - gpr[2] = qemu_semihosting_log_out(s->str, s->len); - g_string_free(s, true); - } else { - gpr[2] = qemu_semihosting_log_out(p, strlen(p)); + { + target_ulong addr = gpr[4]; + ssize_t len = target_strlen(addr); + GString *str; + char *pct_d; + + if (len < 0) { + report_fault(env); + } + p = lock_user(VERIFY_READ, addr, len, 1); + if (!p) { + report_fault(env); + } + + pct_d = strstr(p, "%d"); + if (!pct_d) { + unlock_user(p, addr, 0); + semihost_sys_write(cs, uhi_cb, 2, addr, len); + break; + } + + str = g_string_new_len(p, pct_d - p); + g_string_append_printf(str, "%d%s", (int)gpr[5], pct_d + 2); + unlock_user(p, addr, 0); + + /* + * When we're using gdb, we need a guest address, so + * drop the string onto the stack below the stack pointer. + */ + if (use_gdb_syscalls()) { + addr = gpr[29] - str->len; + p = lock_user(VERIFY_WRITE, addr, str->len, 0); + memcpy(p, str->str, str->len); + unlock_user(p, addr, str->len); + semihost_sys_write(cs, uhi_cb, 2, addr, str->len); + } else { + gpr[2] = qemu_semihosting_console_write(str->str, str->len); + } + g_string_free(str, true); } - FREE_TARGET_STRING(p, gpr[4]); break; + case UHI_assert: - GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); - printf("assertion '"); - printf("\"%s\"", p); - printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); - FREE_TARGET_STRING(p2, gpr[5]); - FREE_TARGET_STRING(p, gpr[4]); - abort(); - break; - case UHI_pread: - gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); - gpr[3] = errno_mips(errno); - break; - case UHI_pwrite: - gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); - gpr[3] = errno_mips(errno); - break; -#ifndef _WIN32 - case UHI_link: - GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); - gpr[2] = link(p, p2); - gpr[3] = errno_mips(errno); - FREE_TARGET_STRING(p2, gpr[5]); - FREE_TARGET_STRING(p, gpr[4]); - break; -#endif + { + const char *msg, *file; + + msg = lock_user_string(gpr[4]); + if (!msg) { + msg = ""; + } + file = lock_user_string(gpr[5]); + if (!file) { + file = ""; + } + + error_report("UHI assertion \"%s\": file \"%s\", line %d", + msg, file, (int)gpr[6]); + abort(); + } + default: - fprintf(stderr, "Unknown UHI operation %d\n", op); + error_report("Unknown UHI operation %d", op); abort(); } return; diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 73254d19298..57ffad2902e 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -1053,6 +1053,10 @@ void mips_cpu_do_interrupt(CPUState *cs) } offset = 0x180; switch (cs->exception_index) { + case EXCP_SEMIHOST: + cs->exception_index = EXCP_NONE; + mips_semihosting(env); + return; case EXCP_DSS: env->CP0_Debug |= 1 << CP0DB_DSS; /* diff --git a/target/mips/tcg/sysemu_helper.h.inc b/target/mips/tcg/sysemu_helper.h.inc index 4353a966f97..af585b5d9c1 100644 --- a/target/mips/tcg/sysemu_helper.h.inc +++ b/target/mips/tcg/sysemu_helper.h.inc @@ -9,8 +9,6 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -DEF_HELPER_1(do_semihosting, void, env) - /* CP0 helpers */ DEF_HELPER_1(mfc0_mvpcontrol, tl, env) DEF_HELPER_1(mfc0_mvpconf0, tl, env) diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index 993720b00c3..1d27fa2ff92 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -62,6 +62,8 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void mips_semihosting(CPUMIPSState *env); + #endif /* !CONFIG_USER_ONLY */ #endif diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 5f460fb687e..1f6a7798081 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -12094,14 +12094,6 @@ static inline bool is_uhi(int sdbbp_code) #endif } -#ifdef CONFIG_USER_ONLY -/* The above should dead-code away any calls to this..*/ -static inline void gen_helper_do_semihosting(void *env) -{ - g_assert_not_reached(); -} -#endif - void gen_ldxs(DisasContext *ctx, int base, int index, int rd) { TCGv t0 = tcg_temp_new(); @@ -13910,7 +13902,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) break; case R6_OPC_SDBBP: if (is_uhi(extract32(ctx->opcode, 6, 20))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -14322,7 +14314,7 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) break; case OPC_SDBBP: if (is_uhi(extract32(ctx->opcode, 6, 20))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { /* * XXX: not clear which exception should be raised @@ -15963,6 +15955,11 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (cpu_supports_isa(env, INSN_VR54XX) && decode_ext_vr54xx(ctx, ctx->opcode)) { return; } +#if defined(TARGET_MIPS64) + if (cpu_supports_isa(env, INSN_OCTEON) && decode_ext_octeon(ctx, ctx->opcode)) { + return; + } +#endif /* ISA extensions */ if (ase_msa_available(env) && decode_ase_msa(ctx, ctx->opcode)) { diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 9997fe2f3c2..55053226aec 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -215,6 +215,7 @@ bool decode_ase_msa(DisasContext *ctx, uint32_t insn); bool decode_ext_txx9(DisasContext *ctx, uint32_t insn); #if defined(TARGET_MIPS64) bool decode_ext_tx79(DisasContext *ctx, uint32_t insn); +bool decode_ext_octeon(DisasContext *ctx, uint32_t insn); #endif bool decode_ext_vr54xx(DisasContext *ctx, uint32_t insn); diff --git a/target/nios2/meson.build b/target/nios2/meson.build index 2bd60ba306a..c6e2243cc3a 100644 --- a/target/nios2/meson.build +++ b/target/nios2/meson.build @@ -1,7 +1,6 @@ nios2_ss = ss.source_set() nios2_ss.add(files( 'cpu.c', - 'nios2-semi.c', 'op_helper.c', 'translate.c', )) @@ -10,7 +9,8 @@ nios2_softmmu_ss = ss.source_set() nios2_softmmu_ss.add(files( 'helper.c', 'monitor.c', - 'mmu.c' + 'mmu.c', + 'nios2-semi.c', )) target_arch += {'nios2': nios2_ss} diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index ec88474a731..55061bb2dcb 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -22,14 +22,9 @@ */ #include "qemu/osdep.h" - #include "cpu.h" #include "exec/gdbstub.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#else -#include "exec/softmmu-semi.h" -#endif +#include "semihosting/softmmu-uaccess.h" #include "qemu/log.h" #define HOSTED_EXIT 0 @@ -47,38 +42,6 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct nios2_gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - -#define GDB_O_RDONLY 0x0 -#define GDB_O_WRONLY 0x1 -#define GDB_O_RDWR 0x2 -#define GDB_O_APPEND 0x8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - static int translate_openflags(int flags) { int hf; @@ -110,9 +73,9 @@ static int translate_openflags(int flags) static bool translate_stat(CPUNios2State *env, target_ulong addr, struct stat *s) { - struct nios2_gdb_stat *p; + struct gdb_stat *p; - p = lock_user(VERIFY_WRITE, addr, sizeof(struct nios2_gdb_stat), 0); + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); if (!p) { return false; @@ -136,14 +99,16 @@ static bool translate_stat(CPUNios2State *env, target_ulong addr, p->gdb_st_atime = cpu_to_be32(s->st_atime); p->gdb_st_mtime = cpu_to_be32(s->st_mtime); p->gdb_st_ctime = cpu_to_be32(s->st_ctime); - unlock_user(p, addr, sizeof(struct nios2_gdb_stat)); + unlock_user(p, addr, sizeof(struct gdb_stat)); return true; } -static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, - uint32_t err) +static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { + 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(err, args + 4)) { /* @@ -156,10 +121,12 @@ static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, } } -static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, - uint32_t err) +static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { + 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(err, args + 8)) { @@ -169,25 +136,6 @@ static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, } } -static int nios2_semi_is_lseek; - -static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - - if (nios2_semi_is_lseek) { - /* - * FIXME: We've already lost the high bits of the lseek - * return value. - */ - nios2_semi_return_u64(env, ret, err); - nios2_semi_is_lseek = 0; - } else { - nios2_semi_return_u32(env, ret, err); - } -} - /* * Read the input value from the argument block; fail the semihosting * call if the memory read fails. @@ -202,6 +150,7 @@ static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) void do_nios2_semihosting(CPUNios2State *env) { + CPUState *cs = env_cpu(env); int nr; uint32_t args; target_ulong arg0, arg1, arg2, arg3; @@ -222,7 +171,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, + gdb_do_syscall(nios2_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1, arg2, arg3); return; } else { @@ -243,7 +192,7 @@ void do_nios2_semihosting(CPUNios2State *env) int fd = arg0; if (fd > 2) { if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "close,%x", arg0); + gdb_do_syscall(nios2_semi_u32_cb, "close,%x", arg0); return; } else { result = close(fd); @@ -259,7 +208,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "read,%x,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "read,%x,%x,%x", arg0, arg1, len); return; } else { @@ -279,7 +228,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "write,%x,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "write,%x,%x,%x", arg0, arg1, len); return; } else { @@ -302,12 +251,11 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(3); off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); if (use_gdb_syscalls()) { - nios2_semi_is_lseek = 1; - gdb_do_syscall(nios2_semi_cb, "lseek,%x,%lx,%x", + gdb_do_syscall(nios2_semi_u64_cb, "lseek,%x,%lx,%x", arg0, off, arg3); } else { off = lseek(arg0, off, arg3); - nios2_semi_return_u64(env, off, errno); + nios2_semi_u64_cb(cs, off, errno); } return; } @@ -317,7 +265,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "rename,%s,%s", + gdb_do_syscall(nios2_semi_u32_cb, "rename,%s,%s", arg0, (int)arg1, arg2, (int)arg3); return; } else { @@ -337,7 +285,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "unlink,%s", + gdb_do_syscall(nios2_semi_u32_cb, "unlink,%s", arg0, (int)arg1); return; } else { @@ -356,7 +304,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(1); GET_ARG(2); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "stat,%s,%x", + gdb_do_syscall(nios2_semi_u32_cb, "stat,%s,%x", arg0, (int)arg1, arg2); return; } else { @@ -379,7 +327,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "fstat,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "fstat,%x,%x", arg0, arg1); return; } else { @@ -395,7 +343,7 @@ void do_nios2_semihosting(CPUNios2State *env) /* Only the tv parameter is used. tz is assumed NULL. */ GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "gettimeofday,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "gettimeofday,%x,%x", arg0, 0); return; } else { @@ -416,7 +364,7 @@ void do_nios2_semihosting(CPUNios2State *env) case HOSTED_ISATTY: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "isatty,%x", arg0); + gdb_do_syscall(nios2_semi_u32_cb, "isatty,%x", arg0); return; } else { result = isatty(arg0); @@ -426,7 +374,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "system,%s", + gdb_do_syscall(nios2_semi_u32_cb, "system,%s", arg0, (int)arg1); return; } else { @@ -446,5 +394,5 @@ void do_nios2_semihosting(CPUNios2State *env) result = 0; } failed: - nios2_semi_return_u32(env, result, errno); + nios2_semi_u32_cb(cs, result, errno); } diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 976be5e0d17..912b037c635 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -385,19 +385,19 @@ POWERPC_DEF_SVR("mpc8548e_v21", "MPC8548E v2.1", CPU_POWERPC_MPC8548E_v21, POWERPC_SVR_8548E_v21, e500v2) POWERPC_DEF_SVR("mpc8555_v10", "MPC8555 v1.0", - CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v2) + CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v1) POWERPC_DEF_SVR("mpc8555_v11", "MPC8555 v1.1", - CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v2) + CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v1) POWERPC_DEF_SVR("mpc8555e_v10", "MPC8555E v1.0", - CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v2) + CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v1) POWERPC_DEF_SVR("mpc8555e_v11", "MPC8555E v1.1", - CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v2) + CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v1) POWERPC_DEF_SVR("mpc8560_v10", "MPC8560 v1.0", - CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v2) + CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v1) POWERPC_DEF_SVR("mpc8560_v20", "MPC8560 v2.0", - CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v2) + CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v1) POWERPC_DEF_SVR("mpc8560_v21", "MPC8560 v2.1", - CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v2) + CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v1) POWERPC_DEF_SVR("mpc8567", "MPC8567", CPU_POWERPC_MPC8567, POWERPC_SVR_8567, e500v2) POWERPC_DEF_SVR("mpc8567e", "MPC8567E", @@ -879,7 +879,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "755", "755_v2.8" }, { "goldfinger", "755_v2.8" }, { "7400", "7400_v2.9" }, - { "max", "7400_v2.9" }, { "g4", "7400_v2.9" }, { "7410", "7410_v1.4" }, { "nitro", "7410_v1.4" }, @@ -918,6 +917,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { #endif { "ppc32", "604" }, { "ppc", "604" }, - { "default", "604" }, + { NULL, NULL } }; diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index 76775a74a9b..1326493a9a0 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -184,13 +184,13 @@ enum { #define CPU_POWERPC_MPC8548E_v11 CPU_POWERPC_e500v2_v11 #define CPU_POWERPC_MPC8548E_v20 CPU_POWERPC_e500v2_v20 #define CPU_POWERPC_MPC8548E_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v1_v10 +#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v1_v20 #define CPU_POWERPC_MPC8567 CPU_POWERPC_e500v2_v22 #define CPU_POWERPC_MPC8567E CPU_POWERPC_e500v2_v22 #define CPU_POWERPC_MPC8568 CPU_POWERPC_e500v2_v22 diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 6d78078f379..a4c893cfadb 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -47,6 +47,18 @@ PPC_BIT32(bs)) #define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) | PPC_BIT8(bs)) +/* + * QEMU version of the GETFIELD/SETFIELD macros from skiboot + * + * It might be better to use the existing extract64() and + * deposit64() but this means that all the register definitions will + * change and become incompatible with the ones found in skiboot. + */ +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) +#define SETFIELD(m, v, val) \ + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) + /*****************************************************************************/ /* Exception vectors definitions */ enum { @@ -694,42 +706,42 @@ enum { /*****************************************************************************/ /* Floating point status and control register */ -#define FPSCR_DRN2 34 /* Decimal Floating-Point rounding control */ -#define FPSCR_DRN1 33 /* Decimal Floating-Point rounding control */ -#define FPSCR_DRN0 32 /* Decimal Floating-Point rounding control */ -#define FPSCR_FX 31 /* Floating-point exception summary */ -#define FPSCR_FEX 30 /* Floating-point enabled exception summary */ -#define FPSCR_VX 29 /* Floating-point invalid operation exception summ. */ -#define FPSCR_OX 28 /* Floating-point overflow exception */ -#define FPSCR_UX 27 /* Floating-point underflow exception */ -#define FPSCR_ZX 26 /* Floating-point zero divide exception */ -#define FPSCR_XX 25 /* Floating-point inexact exception */ -#define FPSCR_VXSNAN 24 /* Floating-point invalid operation exception (sNan) */ -#define FPSCR_VXISI 23 /* Floating-point invalid operation exception (inf) */ -#define FPSCR_VXIDI 22 /* Floating-point invalid operation exception (inf) */ -#define FPSCR_VXZDZ 21 /* Floating-point invalid operation exception (zero) */ -#define FPSCR_VXIMZ 20 /* Floating-point invalid operation exception (inf) */ -#define FPSCR_VXVC 19 /* Floating-point invalid operation exception (comp) */ -#define FPSCR_FR 18 /* Floating-point fraction rounded */ -#define FPSCR_FI 17 /* Floating-point fraction inexact */ -#define FPSCR_C 16 /* Floating-point result class descriptor */ -#define FPSCR_FL 15 /* Floating-point less than or negative */ -#define FPSCR_FG 14 /* Floating-point greater than or negative */ -#define FPSCR_FE 13 /* Floating-point equal or zero */ -#define FPSCR_FU 12 /* Floating-point unordered or NaN */ -#define FPSCR_FPCC 12 /* Floating-point condition code */ -#define FPSCR_FPRF 12 /* Floating-point result flags */ -#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */ -#define FPSCR_VXSQRT 9 /* Floating-point invalid operation exception (sqrt) */ -#define FPSCR_VXCVI 8 /* Floating-point invalid operation exception (int) */ -#define FPSCR_VE 7 /* Floating-point invalid operation exception enable */ -#define FPSCR_OE 6 /* Floating-point overflow exception enable */ -#define FPSCR_UE 5 /* Floating-point underflow exception enable */ -#define FPSCR_ZE 4 /* Floating-point zero divide exception enable */ -#define FPSCR_XE 3 /* Floating-point inexact exception enable */ -#define FPSCR_NI 2 /* Floating-point non-IEEE mode */ -#define FPSCR_RN1 1 -#define FPSCR_RN0 0 /* Floating-point rounding control */ +#define FPSCR_DRN2 PPC_BIT_NR(29) /* Decimal Floating-Point rounding ctrl. */ +#define FPSCR_DRN1 PPC_BIT_NR(30) /* Decimal Floating-Point rounding ctrl. */ +#define FPSCR_DRN0 PPC_BIT_NR(31) /* Decimal Floating-Point rounding ctrl. */ +#define FPSCR_FX PPC_BIT_NR(32) /* Floating-point exception summary */ +#define FPSCR_FEX PPC_BIT_NR(33) /* Floating-point enabled exception summ.*/ +#define FPSCR_VX PPC_BIT_NR(34) /* Floating-point invalid op. excp. summ.*/ +#define FPSCR_OX PPC_BIT_NR(35) /* Floating-point overflow exception */ +#define FPSCR_UX PPC_BIT_NR(36) /* Floating-point underflow exception */ +#define FPSCR_ZX PPC_BIT_NR(37) /* Floating-point zero divide exception */ +#define FPSCR_XX PPC_BIT_NR(38) /* Floating-point inexact exception */ +#define FPSCR_VXSNAN PPC_BIT_NR(39) /* Floating-point invalid op. excp (sNan)*/ +#define FPSCR_VXISI PPC_BIT_NR(40) /* Floating-point invalid op. excp (inf) */ +#define FPSCR_VXIDI PPC_BIT_NR(41) /* Floating-point invalid op. excp (inf) */ +#define FPSCR_VXZDZ PPC_BIT_NR(42) /* Floating-point invalid op. excp (zero)*/ +#define FPSCR_VXIMZ PPC_BIT_NR(43) /* Floating-point invalid op. excp (inf) */ +#define FPSCR_VXVC PPC_BIT_NR(44) /* Floating-point invalid op. excp (comp)*/ +#define FPSCR_FR PPC_BIT_NR(45) /* Floating-point fraction rounded */ +#define FPSCR_FI PPC_BIT_NR(46) /* Floating-point fraction inexact */ +#define FPSCR_C PPC_BIT_NR(47) /* Floating-point result class descriptor*/ +#define FPSCR_FL PPC_BIT_NR(48) /* Floating-point less than or negative */ +#define FPSCR_FG PPC_BIT_NR(49) /* Floating-point greater than or neg. */ +#define FPSCR_FE PPC_BIT_NR(50) /* Floating-point equal or zero */ +#define FPSCR_FU PPC_BIT_NR(51) /* Floating-point unordered or NaN */ +#define FPSCR_FPCC PPC_BIT_NR(51) /* Floating-point condition code */ +#define FPSCR_FPRF PPC_BIT_NR(51) /* Floating-point result flags */ +#define FPSCR_VXSOFT PPC_BIT_NR(53) /* Floating-point invalid op. excp (soft)*/ +#define FPSCR_VXSQRT PPC_BIT_NR(54) /* Floating-point invalid op. excp (sqrt)*/ +#define FPSCR_VXCVI PPC_BIT_NR(55) /* Floating-point invalid op. excp (int) */ +#define FPSCR_VE PPC_BIT_NR(56) /* Floating-point invalid op. excp enable*/ +#define FPSCR_OE PPC_BIT_NR(57) /* Floating-point overflow excp. enable */ +#define FPSCR_UE PPC_BIT_NR(58) /* Floating-point underflow excp. enable */ +#define FPSCR_ZE PPC_BIT_NR(59) /* Floating-point zero divide excp enable*/ +#define FPSCR_XE PPC_BIT_NR(60) /* Floating-point inexact excp. enable */ +#define FPSCR_NI PPC_BIT_NR(61) /* Floating-point non-IEEE mode */ +#define FPSCR_RN1 PPC_BIT_NR(62) +#define FPSCR_RN0 PPC_BIT_NR(63) /* Floating-point rounding control */ /* Invalid operation exception summary */ #define FPSCR_IX ((1 << FPSCR_VXSNAN) | (1 << FPSCR_VXISI) | \ (1 << FPSCR_VXIDI) | (1 << FPSCR_VXZDZ) | \ @@ -1172,7 +1184,6 @@ struct CPUArchState { * by recent Book3s compatible CPUs (POWER7 and newer). */ uint32_t irq_input_state; - void **irq_inputs; target_ulong excp_vectors[POWERPC_EXCP_NB]; /* Exception vectors */ target_ulong excp_prefix; @@ -2203,8 +2214,6 @@ enum { PPC_DCR = 0x1000000000000000ULL, /* DCR extended accesse */ PPC_DCRX = 0x2000000000000000ULL, - /* user-mode DCR access, implemented in PowerPC 460 */ - PPC_DCRUX = 0x4000000000000000ULL, /* popcntw and popcntd instructions */ PPC_POPCNTWD = 0x8000000000000000ULL, @@ -2228,8 +2237,8 @@ enum { | PPC_405_MAC | PPC_440_SPEC | PPC_BOOKE \ | PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \ | PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \ - | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \ - | PPC_POPCNTWD | PPC_CILDST) + | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_POPCNTWD \ + | PPC_CILDST) /* extended type values */ @@ -2277,6 +2286,8 @@ enum { PPC2_ISA310 = 0x0000000000100000ULL, /* lwsync instruction */ PPC2_MEM_LWSYNC = 0x0000000000200000ULL, + /* ISA 2.06 BCD assist instructions */ + PPC2_BCDA_ISA206 = 0x0000000000400000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ @@ -2285,7 +2296,8 @@ enum { PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206 | \ - PPC2_ISA300 | PPC2_ISA310 | PPC2_MEM_LWSYNC) + PPC2_ISA300 | PPC2_ISA310 | PPC2_MEM_LWSYNC | \ + PPC2_BCDA_ISA206) }; /*****************************************************************************/ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 0f891afa048..d1493a660c9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -47,6 +47,10 @@ #include "spr_common.h" #include "power8-pmu.h" +#ifndef CONFIG_USER_ONLY +#include "hw/boards.h" +#endif + /* #define PPC_DEBUG_SPR */ /* #define USE_APPLE_GDB */ @@ -5985,7 +5989,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 | - PPC2_PM_ISA206 | PPC2_MEM_LWSYNC; + PPC2_PM_ISA206 | PPC2_MEM_LWSYNC | PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -6159,7 +6163,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM | PPC2_PM_ISA206 | PPC2_MEM_LWSYNC; + PPC2_TM | PPC2_PM_ISA206 | PPC2_MEM_LWSYNC | + PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_HV) | (1ull << MSR_TM) | @@ -6368,7 +6373,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBSYNC | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD | @@ -6379,7 +6384,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL | PPC2_MEM_LWSYNC; + PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL | PPC2_MEM_LWSYNC | + PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_HV) | (1ull << MSR_TM) | @@ -6585,7 +6591,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBSYNC | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD | @@ -6597,7 +6603,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) 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_MEM_LWSYNC | PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_HV) | (1ull << MSR_TM) | @@ -6672,7 +6678,6 @@ static void init_ppc_proc(PowerPCCPU *cpu) #if !defined(CONFIG_USER_ONLY) int i; - env->irq_inputs = NULL; /* Set all exception vectors to an invalid address */ for (i = 0; i < POWERPC_EXCP_NB; i++) { env->excp_vectors[i] = (target_ulong)(-1ULL); @@ -6802,10 +6807,6 @@ static void init_ppc_proc(PowerPCCPU *cpu) /* Pre-compute some useful values */ env->tlb_per_way = env->nb_tlb / env->nb_ways; } - if (env->irq_inputs == NULL) { - warn_report("no internal IRQ controller registered." - " Attempt QEMU to crash very soon !"); - } #endif if (env->check_pow == NULL) { warn_report("no power management check handler registered." @@ -6963,6 +6964,21 @@ static ObjectClass *ppc_cpu_class_by_name(const char *name) } } + /* + * All ppc CPUs represent hardware that exists in the real world, i.e.: we + * do not have a "max" CPU with all possible emulated features enabled. + * Return the default CPU type for the machine because that has greater + * chance of being useful as the "max" CPU. + */ +#if !defined(CONFIG_USER_ONLY) + if (strcmp(name, "max") == 0) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (mc) { + return object_class_by_name(mc->default_cpu_type); + } + } +#endif + cpu_model = g_ascii_strdown(name, -1); p = ppc_cpu_lookup_alias(cpu_model); if (p) { @@ -7186,6 +7202,9 @@ static void ppc_cpu_reset(DeviceState *dev) } pmu_update_summaries(env); } + + /* clean any pending stop state */ + env->resume_as_sreset = 0; #endif hreg_compute_hflags(env); env->reserve_addr = (target_ulong)-1ULL; @@ -7452,17 +7471,15 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) "%08x iidx %d didx %d\n", env->msr, env->spr[SPR_HID0], env->hflags, cpu_mmu_index(env, true), cpu_mmu_index(env, false)); -#if !defined(NO_TIMER_DUMP) - qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 #if !defined(CONFIG_USER_ONLY) - " DECR " TARGET_FMT_lu -#endif - "\n", - cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) -#if !defined(CONFIG_USER_ONLY) - , cpu_ppc_load_decr(env) -#endif - ); + if (env->tb_env) { + qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 + " DECR " TARGET_FMT_lu "\n", cpu_ppc_load_tbu(env), + cpu_ppc_load_tbl(env), cpu_ppc_load_decr(env)); + } +#else + qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 "\n", cpu_ppc_load_tbu(env), + cpu_ppc_load_tbl(env)); #endif for (i = 0; i < 32; i++) { if ((i & (RGPL - 1)) == 0) { diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index 0d01ac3de0b..5ba74b21245 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -1391,3 +1391,68 @@ DFP_HELPER_SHIFT(DSCLI, 64, 1) DFP_HELPER_SHIFT(DSCLIQ, 128, 1) DFP_HELPER_SHIFT(DSCRI, 64, 0) DFP_HELPER_SHIFT(DSCRIQ, 128, 0) + +target_ulong helper_CDTBCD(target_ulong s) +{ + uint64_t res = 0; + uint32_t dec32, declets; + uint8_t bcd[6]; + int i, w, sh; + decNumber a; + + for (w = 1; w >= 0; w--) { + res <<= 32; + declets = extract64(s, 32 * w, 20); + if (declets) { + /* decimal32 with zero exponent and word "w" declets */ + dec32 = (0x225ULL << 20) | declets; + decimal32ToNumber((decimal32 *)&dec32, &a); + decNumberGetBCD(&a, bcd); + for (i = 0; i < a.digits; i++) { + sh = 4 * (a.digits - 1 - i); + res |= (uint64_t)bcd[i] << sh; + } + } + } + + return res; +} + +target_ulong helper_CBCDTD(target_ulong s) +{ + uint64_t res = 0; + uint32_t dec32; + uint8_t bcd[6]; + int w, i, offs; + decNumber a; + decContext context; + + decContextDefault(&context, DEC_INIT_DECIMAL32); + + for (w = 1; w >= 0; w--) { + res <<= 32; + decNumberZero(&a); + /* Extract each BCD field of word "w" */ + for (i = 5; i >= 0; i--) { + offs = 4 * (5 - i) + 32 * w; + bcd[i] = extract64(s, offs, 4); + if (bcd[i] > 9) { + /* + * If the field value is greater than 9, the results are + * undefined. We could use a fixed value like 0 or 9, but + * an and with 9 seems to better match the hardware behavior. + */ + bcd[i] &= 9; + } + } + + /* Create a decNumber with the BCD values and convert to decimal32 */ + decNumberSetBCD(&a, bcd, 6); + decimal32FromNumber((decimal32 *)&dec32, &a, &context); + + /* Extract the two declets from the decimal32 value */ + res |= dec32 & 0xfffff; + } + + return res; +} diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index fed0ce420a0..7ab6beadad3 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -464,7 +464,7 @@ static void do_fpscr_check_status(CPUPPCState *env, uintptr_t raddr) } cs->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = error | POWERPC_EXCP_FP; - env->fpscr |= error ? FP_FEX : 0; + env->fpscr |= FP_FEX; /* Deferred floating-point exception after target FPSCR update */ if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, cs->exception_index, diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6233e28d853..159b352f6eb 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -54,6 +54,8 @@ DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(CFUGED, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(CDTBCD, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(CBCDTD, TCG_CALL_NO_RWG_SE, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) @@ -175,6 +177,14 @@ DEF_HELPER_FLAGS_3(VMULOSW, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VMULOUB, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VMULOUH, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VMULOUW, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVSQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVESD, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVEUD, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVESQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVEUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMODSQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMODUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vslo, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vsro, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vsrv, TCG_CALL_NO_RWG, void, avr, avr, avr) @@ -196,14 +206,14 @@ DEF_HELPER_FLAGS_5(vadduws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vsububs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vsubuhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vsubuws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_3(vadduqm, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_4(vaddecuq, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_4(vaddeuqm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_3(vaddcuq, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vsubuqm, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_4(vsubecuq, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_4(vsubeuqm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_3(vsubcuq, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VADDUQM, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_4(VADDECUQ, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_4(VADDEUQM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_3(VADDCUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VSUBUQM, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_4(VSUBECUQ, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_4(VSUBEUQM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_3(VSUBCUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_4(vsldoi, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) DEF_HELPER_FLAGS_3(vextractub, TCG_CALL_NO_RWG, void, avr, avr, i32) DEF_HELPER_FLAGS_3(vextractuh, TCG_CALL_NO_RWG, void, avr, avr, i32) @@ -310,7 +320,7 @@ DEF_HELPER_FLAGS_3(vbpermq, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vpmsumb, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vpmsumh, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vpmsumw, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vpmsumd, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VPMSUMD, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_2(vextublx, TCG_CALL_NO_RWG, tl, tl, avr) DEF_HELPER_FLAGS_2(vextuhlx, TCG_CALL_NO_RWG, tl, tl, avr) DEF_HELPER_FLAGS_2(vextuwlx, TCG_CALL_NO_RWG, tl, tl, avr) @@ -664,13 +674,16 @@ DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) -DEF_HELPER_2(load_slb_esid, tl, env, tl) -DEF_HELPER_2(load_slb_vsid, tl, env, tl) -DEF_HELPER_2(find_slb_vsid, tl, env, tl) -DEF_HELPER_FLAGS_2(slbia, TCG_CALL_NO_RWG, void, env, i32) -DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_2(slbieg, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_4(tlbie_isa300, TCG_CALL_NO_WG, void, \ + env, tl, tl, i32) +DEF_HELPER_FLAGS_3(SLBMTE, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_2(SLBMFEE, tl, env, tl) +DEF_HELPER_2(SLBMFEV, tl, env, tl) +DEF_HELPER_2(SLBFEE, tl, env, tl) +DEF_HELPER_FLAGS_2(SLBIA, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_3(SLBIAG, TCG_CALL_NO_RWG, void, env, tl, i32) +DEF_HELPER_FLAGS_2(SLBIE, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(SLBIEG, TCG_CALL_NO_RWG, void, env, tl) #endif DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl) DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) @@ -684,10 +697,10 @@ DEF_HELPER_2(book3s_msgclr, void, env, tl) DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_2(rac, tl, env, tl) -#endif DEF_HELPER_2(load_dcr, tl, env, tl) DEF_HELPER_3(store_dcr, void, env, tl, tl) +#endif DEF_HELPER_2(load_dump_spr, void, env, i32) DEF_HELPER_2(store_dump_spr, void, env, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 18a94fa3b52..eb41efc100e 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -21,11 +21,11 @@ @A ...... frt:5 fra:5 frb:5 frc:5 ..... rc:1 &A &D rt ra si:int64_t -@D ...... rt:5 ra:5 si:s16 &D +@D ...... rt:5 ra:5 si:s16 &D &D_bf bf l:bool ra imm -@D_bfs ...... bf:3 - l:1 ra:5 imm:s16 &D_bf -@D_bfu ...... bf:3 - l:1 ra:5 imm:16 &D_bf +@D_bfs ...... bf:3 . l:1 ra:5 imm:s16 &D_bf +@D_bfu ...... bf:3 . l:1 ra:5 imm:16 &D_bf %dq_si 4:s12 !function=times_16 %dq_rtp 22:4 !function=times_2 @@ -38,7 +38,7 @@ @DQ_TSXP ...... ..... ra:5 ............ .... &D si=%dq_si rt=%rt_tsxp %ds_si 2:s14 !function=times_4 -@DS ...... rt:5 ra:5 .............. .. &D si=%ds_si +@DS ...... rt:5 ra:5 .............. .. &D si=%ds_si %ds_rtp 22:4 !function=times_2 @DS_rtp ...... ....0 ra:5 .............. .. &D rt=%ds_rtp si=%ds_si @@ -49,10 +49,10 @@ &DX rt d %dx_d 6:s10 16:5 0:1 -@DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d +@DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d &VA vrt vra vrb rc -@VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA +@VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA &VC vrt vra vrb rc:bool @VC ...... vrt:5 vra:5 vrb:5 rc:1 .......... &VC @@ -61,7 +61,7 @@ @VN ...... vrt:5 vra:5 vrb:5 .. sh:3 ...... &VN &VX vrt vra vrb -@VX ...... vrt:5 vra:5 vrb:5 .......... . &VX +@VX ...... vrt:5 vra:5 vrb:5 .......... . &VX &VX_bf bf vra vrb @VX_bf ...... bf:3 .. vra:5 vrb:5 ........... &VX_bf @@ -76,17 +76,20 @@ @VX_tb_rc ...... vrt:5 ..... vrb:5 rc:1 .......... &VX_tb_rc &VX_uim4 vrt uim vrb -@VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4 +@VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4 &VX_tb vrt vrb -@VX_tb ...... vrt:5 ..... vrb:5 ........... &VX_tb +@VX_tb ...... vrt:5 ..... vrb:5 ........... &VX_tb &X rt ra rb -@X ...... rt:5 ra:5 rb:5 .......... . &X +@X ...... rt:5 ra:5 rb:5 .......... . &X &X_rc rt ra rb rc:bool @X_rc ...... rt:5 ra:5 rb:5 .......... rc:1 &X_rc +&X_sa rs ra +@X_sa ...... rs:5 ra:5 ..... .......... . &X_sa + %x_frtp 22:4 !function=times_2 %x_frap 17:4 !function=times_2 %x_frbp 12:4 !function=times_2 @@ -94,9 +97,15 @@ @X_tp_a_bp_rc ...... ....0 ra:5 ....0 .......... rc:1 &X_rc rt=%x_frtp rb=%x_frbp +&X_t rt +@X_t ...... rt:5 ..... ..... .......... . &X_t + &X_tb rt rb @X_tb ...... rt:5 ..... rb:5 .......... . &X_tb +&X_t_rc rt rc:bool +@X_t_rc ...... rt:5 ..... ..... .......... rc:1 &X_t_rc + &X_tb_rc rt rb rc:bool @X_tb_rc ...... rt:5 ..... rb:5 .......... rc:1 &X_tb_rc @@ -107,7 +116,7 @@ @X_t_bp_rc ...... rt:5 ..... ....0 .......... rc:1 &X_tb_rc rb=%x_frbp &X_bi rt bi -@X_bi ...... rt:5 bi:5 ----- .......... - &X_bi +@X_bi ...... rt:5 bi:5 ..... .......... . &X_bi &X_bf bf ra rb @X_bf ...... bf:3 .. ra:5 rb:5 .......... . &X_bf @@ -122,7 +131,13 @@ @X_bf_uim_bp ...... bf:3 . uim:6 ....0 .......... . &X_bf_uim rb=%x_frbp &X_bfl bf l:bool ra rb -@X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl +@X_bfl ...... bf:3 . l:1 ra:5 rb:5 .......... . &X_bfl + +&X_imm2 rt imm +@X_imm2 ...... rt:5 ..... ... imm:2 .......... . &X_imm2 + +&X_imm3 rt imm +@X_imm3 ...... rt:5 ..... .. imm:3 .......... . &X_imm3 %x_xt 0:1 21:5 &X_imm5 xt imm:uint8_t vrb @@ -131,6 +146,15 @@ &X_imm8 xt imm:uint8_t @X_imm8 ...... ..... .. imm:8 .......... . &X_imm8 xt=%x_xt +&X_ih ih:uint8_t +@X_ih ...... .. ih:3 ..... ..... .......... . &X_ih + +&X_rb rb +@X_rb ...... ..... ..... rb:5 .......... . &X_rb + +&X_rs_l rs l:bool +@X_rs_l ...... rs:5 .... l:1 ..... .......... . &X_rs_l + &X_uim5 xt uim:uint8_t @X_uim5 ...... ..... ..... uim:5 .......... . &X_uim5 xt=%x_xt @@ -299,6 +323,12 @@ CNTTZDM 011111 ..... ..... ..... 1000111011 - @X PDEPD 011111 ..... ..... ..... 0010011100 - @X PEXTD 011111 ..... ..... ..... 0010111100 - @X +## BCD Assist + +ADDG6S 011111 ..... ..... ..... - 001001010 - @X +CDTBCD 011111 ..... ..... ----- 0100011010 - @X_sa +CBCDTD 011111 ..... ..... ----- 0100111010 - @X_sa + ### Float-Point Load Instructions LFS 110000 ..... ..... ................ @D @@ -334,6 +364,16 @@ SETBCR 011111 ..... ..... ----- 0110100000 - @X_bi SETNBC 011111 ..... ..... ----- 0111000000 - @X_bi SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi +### Move To/From FPSCR + +MFFS 111111 ..... 00000 ----- 1001000111 . @X_t_rc +MFFSCE 111111 ..... 00001 ----- 1001000111 - @X_t +MFFSCRN 111111 ..... 10110 ..... 1001000111 - @X_tb +MFFSCDRN 111111 ..... 10100 ..... 1001000111 - @X_tb +MFFSCRNI 111111 ..... 10111 ---.. 1001000111 - @X_imm2 +MFFSCDRNI 111111 ..... 10101 --... 1001000111 - @X_imm3 +MFFSL 111111 ..... 11000 ----- 1001000111 - @X_t + ### Decimal Floating-Point Arithmetic Instructions DADD 111011 ..... ..... ..... 0000000010 . @X_rc @@ -426,6 +466,10 @@ DSCLIQ 111111 ..... ..... ...... 001000010 . @Z22_tap_sh_rc DSCRI 111011 ..... ..... ...... 001100010 . @Z22_ta_sh_rc DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc +## Vector Exclusive-OR-based Instructions + +VPMSUMD 000100 ..... ..... ..... 10011001000 @VX + ## Vector Integer Instructions VCMPEQUB 000100 ..... ..... ..... . 0000000110 @VC @@ -546,6 +590,18 @@ VRLQNM 000100 ..... ..... ..... 00101000101 @VX ## Vector Integer Arithmetic Instructions +VADDCUQ 000100 ..... ..... ..... 00101000000 @VX +VADDUQM 000100 ..... ..... ..... 00100000000 @VX + +VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA +VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA + +VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX +VSUBUQM 000100 ..... ..... ..... 10100000000 @VX + +VSUBECUQ 000100 ..... ..... ..... ..... 111111 @VA +VSUBEUQM 000100 ..... ..... ..... ..... 111110 @VA + VEXTSB2W 000100 ..... 10000 ..... 11000000010 @VX_tb VEXTSH2W 000100 ..... 10001 ..... 11000000010 @VX_tb VEXTSB2D 000100 ..... 11000 ..... 11000000010 @VX_tb @@ -786,3 +842,51 @@ XVF64GERPP 111011 ... -- .... 0 ..... 00111010 ..- @XX3_at xa=%xx_xa_pair XVF64GERPN 111011 ... -- .... 0 ..... 10111010 ..- @XX3_at xa=%xx_xa_pair XVF64GERNP 111011 ... -- .... 0 ..... 01111010 ..- @XX3_at xa=%xx_xa_pair XVF64GERNN 111011 ... -- .... 0 ..... 11111010 ..- @XX3_at xa=%xx_xa_pair + +## Vector Division Instructions + +VDIVSW 000100 ..... ..... ..... 00110001011 @VX +VDIVUW 000100 ..... ..... ..... 00010001011 @VX +VDIVSD 000100 ..... ..... ..... 00111001011 @VX +VDIVUD 000100 ..... ..... ..... 00011001011 @VX +VDIVSQ 000100 ..... ..... ..... 00100001011 @VX +VDIVUQ 000100 ..... ..... ..... 00000001011 @VX + +VDIVESW 000100 ..... ..... ..... 01110001011 @VX +VDIVEUW 000100 ..... ..... ..... 01010001011 @VX +VDIVESD 000100 ..... ..... ..... 01111001011 @VX +VDIVEUD 000100 ..... ..... ..... 01011001011 @VX +VDIVESQ 000100 ..... ..... ..... 01100001011 @VX +VDIVEUQ 000100 ..... ..... ..... 01000001011 @VX + +VMODSW 000100 ..... ..... ..... 11110001011 @VX +VMODUW 000100 ..... ..... ..... 11010001011 @VX +VMODSD 000100 ..... ..... ..... 11111001011 @VX +VMODUD 000100 ..... ..... ..... 11011001011 @VX +VMODSQ 000100 ..... ..... ..... 11100001011 @VX +VMODUQ 000100 ..... ..... ..... 11000001011 @VX + +## SLB Management Instructions + +SLBIE 011111 ----- ----- ..... 0110110010 - @X_rb +SLBIEG 011111 ..... ----- ..... 0111010010 - @X_tb + +SLBIA 011111 --... ----- ----- 0111110010 - @X_ih +SLBIAG 011111 ..... ----. ----- 1101010010 - @X_rs_l + +SLBMTE 011111 ..... ----- ..... 0110010010 - @X_tb + +SLBMFEV 011111 ..... ----- ..... 1101010011 - @X_tb +SLBMFEE 011111 ..... ----- ..... 1110010011 - @X_tb + +SLBFEE 011111 ..... ----- ..... 1111010011 1 @X_tb + +SLBSYNC 011111 ----- ----- ----- 0101010010 - + +## TLB Management Instructions + +&X_tlbie rb rs ric prs:bool r:bool +@X_tlbie ...... rs:5 - ric:2 prs:1 r:1 rb:5 .......... - &X_tlbie + +TLBIE 011111 ..... - .. . . ..... 0100110010 - @X_tlbie +TLBIEL 011111 ..... - .. . . ..... 0100010010 - @X_tlbie diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 105b626d1b5..d905f07d027 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -789,7 +789,7 @@ static int64_t ger_rank8(uint32_t a, uint32_t b, uint32_t mask) int64_t psum = 0; for (int i = 0; i < 8; i++, mask >>= 1) { if (mask & 1) { - psum += sextract32(a, 4 * i, 4) * sextract32(b, 4 * i, 4); + psum += (int64_t)sextract32(a, 4 * i, 4) * sextract32(b, 4 * i, 4); } } return psum; @@ -811,7 +811,8 @@ static int64_t ger_rank2(uint32_t a, uint32_t b, uint32_t mask) int64_t psum = 0; for (int i = 0; i < 2; i++, mask >>= 1) { if (mask & 1) { - psum += sextract32(a, 16 * i, 16) * sextract32(b, 16 * i, 16); + psum += (int64_t)sextract32(a, 16 * i, 16) * + sextract32(b, 16 * i, 16); } } return psum; @@ -1162,6 +1163,112 @@ void helper_XXPERMX(ppc_vsr_t *t, ppc_vsr_t *s0, ppc_vsr_t *s1, ppc_vsr_t *pcv, *t = tmp; } +void helper_VDIVSQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 neg1 = int128_makes64(-1); + Int128 int128_min = int128_make128(0, INT64_MIN); + if (likely(int128_nz(b->s128) && + (int128_ne(a->s128, int128_min) || int128_ne(b->s128, neg1)))) { + t->s128 = int128_divs(a->s128, b->s128); + } else { + t->s128 = a->s128; /* Undefined behavior */ + } +} + +void helper_VDIVUQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + if (int128_nz(b->s128)) { + t->s128 = int128_divu(a->s128, b->s128); + } else { + t->s128 = a->s128; /* Undefined behavior */ + } +} + +void helper_VDIVESD(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + int64_t high; + uint64_t low; + for (i = 0; i < 2; i++) { + high = a->s64[i]; + low = 0; + if (unlikely((high == INT64_MIN && b->s64[i] == -1) || !b->s64[i])) { + t->s64[i] = a->s64[i]; /* Undefined behavior */ + } else { + divs128(&low, &high, b->s64[i]); + t->s64[i] = low; + } + } +} + +void helper_VDIVEUD(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + uint64_t high, low; + for (i = 0; i < 2; i++) { + high = a->u64[i]; + low = 0; + if (unlikely(!b->u64[i])) { + t->u64[i] = a->u64[i]; /* Undefined behavior */ + } else { + divu128(&low, &high, b->u64[i]); + t->u64[i] = low; + } + } +} + +void helper_VDIVESQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 high, low; + Int128 int128_min = int128_make128(0, INT64_MIN); + Int128 neg1 = int128_makes64(-1); + + high = a->s128; + low = int128_zero(); + if (unlikely(!int128_nz(b->s128) || + (int128_eq(b->s128, neg1) && int128_eq(high, int128_min)))) { + t->s128 = a->s128; /* Undefined behavior */ + } else { + divs256(&low, &high, b->s128); + t->s128 = low; + } +} + +void helper_VDIVEUQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 high, low; + + high = a->s128; + low = int128_zero(); + if (unlikely(!int128_nz(b->s128))) { + t->s128 = a->s128; /* Undefined behavior */ + } else { + divu256(&low, &high, b->s128); + t->s128 = low; + } +} + +void helper_VMODSQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 neg1 = int128_makes64(-1); + Int128 int128_min = int128_make128(0, INT64_MIN); + if (likely(int128_nz(b->s128) && + (int128_ne(a->s128, int128_min) || int128_ne(b->s128, neg1)))) { + t->s128 = int128_rems(a->s128, b->s128); + } else { + t->s128 = int128_zero(); /* Undefined behavior */ + } +} + +void helper_VMODUQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + if (likely(int128_nz(b->s128))) { + t->s128 = int128_remu(a->s128, b->s128); + } else { + t->s128 = int128_zero(); /* Undefined behavior */ + } +} + void helper_VPERM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { ppc_avr_t result; @@ -1307,14 +1414,13 @@ XXGENPCV(XXGENPCVDM, 8) #define VBPERMQ_INDEX(avr, i) ((avr)->u8[(i)]) #define VBPERMD_INDEX(i) (i) #define VBPERMQ_DW(index) (((index) & 0x40) != 0) -#define EXTRACT_BIT(avr, i, index) (extract64((avr)->u64[i], index, 1)) #else #define VBPERMQ_INDEX(avr, i) ((avr)->u8[15 - (i)]) #define VBPERMD_INDEX(i) (1 - i) #define VBPERMQ_DW(index) (((index) & 0x40) == 0) -#define EXTRACT_BIT(avr, i, index) \ - (extract64((avr)->u64[1 - i], 63 - index, 1)) #endif +#define EXTRACT_BIT(avr, i, index) \ + (extract64((avr)->VsrD(i), 63 - index, 1)) void helper_vbpermd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { @@ -1378,52 +1484,24 @@ PMSUM(vpmsumb, u8, u16, uint16_t) PMSUM(vpmsumh, u16, u32, uint32_t) PMSUM(vpmsumw, u32, u64, uint64_t) -void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +void helper_VPMSUMD(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - -#ifdef CONFIG_INT128 int i, j; - __uint128_t prod[2]; - - VECTOR_FOR_INORDER_I(i, u64) { - prod[i] = 0; - for (j = 0; j < 64; j++) { - if (a->u64[i] & (1ull << j)) { - prod[i] ^= (((__uint128_t)b->u64[i]) << j); + Int128 tmp, prod[2] = {int128_zero(), int128_zero()}; + + for (j = 0; j < 64; j++) { + for (i = 0; i < ARRAY_SIZE(r->u64); i++) { + if (a->VsrD(i) & (1ull << j)) { + tmp = int128_make64(b->VsrD(i)); + tmp = int128_lshift(tmp, j); + prod[i] = int128_xor(prod[i], tmp); } } } - r->u128 = prod[0] ^ prod[1]; - -#else - int i, j; - ppc_avr_t prod[2]; - - VECTOR_FOR_INORDER_I(i, u64) { - prod[i].VsrD(1) = prod[i].VsrD(0) = 0; - for (j = 0; j < 64; j++) { - if (a->u64[i] & (1ull << j)) { - ppc_avr_t bshift; - if (j == 0) { - bshift.VsrD(0) = 0; - bshift.VsrD(1) = b->u64[i]; - } else { - bshift.VsrD(0) = b->u64[i] >> (64 - j); - bshift.VsrD(1) = b->u64[i] << j; - } - prod[i].VsrD(1) ^= bshift.VsrD(1); - prod[i].VsrD(0) ^= bshift.VsrD(0); - } - } - } - - r->VsrD(1) = prod[0].VsrD(1) ^ prod[1].VsrD(1); - r->VsrD(0) = prod[0].VsrD(0) ^ prod[1].VsrD(0); -#endif + r->s128 = int128_xor(prod[0], prod[1]); } - #if HOST_BIG_ENDIAN #define PKBIG 1 #else @@ -2098,189 +2176,66 @@ VGENERIC_DO(popcntd, u64) #undef VGENERIC_DO -#if HOST_BIG_ENDIAN -#define QW_ONE { .u64 = { 0, 1 } } -#else -#define QW_ONE { .u64 = { 1, 0 } } -#endif - -#ifndef CONFIG_INT128 - -static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a) -{ - t->u64[0] = ~a.u64[0]; - t->u64[1] = ~a.u64[1]; -} - -static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) -{ - if (a.VsrD(0) < b.VsrD(0)) { - return -1; - } else if (a.VsrD(0) > b.VsrD(0)) { - return 1; - } else if (a.VsrD(1) < b.VsrD(1)) { - return -1; - } else if (a.VsrD(1) > b.VsrD(1)) { - return 1; - } else { - return 0; - } -} - -static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) -{ - t->VsrD(1) = a.VsrD(1) + b.VsrD(1); - t->VsrD(0) = a.VsrD(0) + b.VsrD(0) + - (~a.VsrD(1) < b.VsrD(1)); -} - -static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +void helper_VADDUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - ppc_avr_t not_a; - t->VsrD(1) = a.VsrD(1) + b.VsrD(1); - t->VsrD(0) = a.VsrD(0) + b.VsrD(0) + - (~a.VsrD(1) < b.VsrD(1)); - avr_qw_not(¬_a, a); - return avr_qw_cmpu(not_a, b) < 0; + r->s128 = int128_add(a->s128, b->s128); } -#endif - -void helper_vadduqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +void helper_VADDEUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { -#ifdef CONFIG_INT128 - r->u128 = a->u128 + b->u128; -#else - avr_qw_add(r, *a, *b); -#endif + r->s128 = int128_add(int128_add(a->s128, b->s128), + int128_make64(int128_getlo(c->s128) & 1)); } -void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +void helper_VADDCUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { -#ifdef CONFIG_INT128 - r->u128 = a->u128 + b->u128 + (c->u128 & 1); -#else - - if (c->VsrD(1) & 1) { - ppc_avr_t tmp; - - tmp.VsrD(0) = 0; - tmp.VsrD(1) = c->VsrD(1) & 1; - avr_qw_add(&tmp, *a, tmp); - avr_qw_add(r, tmp, *b); - } else { - avr_qw_add(r, *a, *b); - } -#endif -} - -void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ -#ifdef CONFIG_INT128 - r->u128 = (~a->u128 < b->u128); -#else - ppc_avr_t not_a; - - avr_qw_not(¬_a, *a); - + r->VsrD(1) = int128_ult(int128_not(a->s128), b->s128); r->VsrD(0) = 0; - r->VsrD(1) = (avr_qw_cmpu(not_a, *b) < 0); -#endif } -void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +void helper_VADDECUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { -#ifdef CONFIG_INT128 - int carry_out = (~a->u128 < b->u128); - if (!carry_out && (c->u128 & 1)) { - carry_out = ((a->u128 + b->u128 + 1) == 0) && - ((a->u128 != 0) || (b->u128 != 0)); - } - r->u128 = carry_out; -#else - - int carry_in = c->VsrD(1) & 1; - int carry_out = 0; - ppc_avr_t tmp; - - carry_out = avr_qw_addc(&tmp, *a, *b); + bool carry_out = int128_ult(int128_not(a->s128), b->s128), + carry_in = int128_getlo(c->s128) & 1; if (!carry_out && carry_in) { - ppc_avr_t one = QW_ONE; - carry_out = avr_qw_addc(&tmp, tmp, one); + carry_out = (int128_nz(a->s128) || int128_nz(b->s128)) && + int128_eq(int128_add(a->s128, b->s128), int128_makes64(-1)); } + r->VsrD(0) = 0; r->VsrD(1) = carry_out; -#endif } -void helper_vsubuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +void helper_VSUBUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { -#ifdef CONFIG_INT128 - r->u128 = a->u128 - b->u128; -#else - ppc_avr_t tmp; - ppc_avr_t one = QW_ONE; - - avr_qw_not(&tmp, *b); - avr_qw_add(&tmp, *a, tmp); - avr_qw_add(r, tmp, one); -#endif + r->s128 = int128_sub(a->s128, b->s128); } -void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +void helper_VSUBEUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { -#ifdef CONFIG_INT128 - r->u128 = a->u128 + ~b->u128 + (c->u128 & 1); -#else - ppc_avr_t tmp, sum; - - avr_qw_not(&tmp, *b); - avr_qw_add(&sum, *a, tmp); - - tmp.VsrD(0) = 0; - tmp.VsrD(1) = c->VsrD(1) & 1; - avr_qw_add(r, sum, tmp); -#endif + r->s128 = int128_add(int128_add(a->s128, int128_not(b->s128)), + int128_make64(int128_getlo(c->s128) & 1)); } -void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +void helper_VSUBCUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { -#ifdef CONFIG_INT128 - r->u128 = (~a->u128 < ~b->u128) || - (a->u128 + ~b->u128 == (__uint128_t)-1); -#else - int carry = (avr_qw_cmpu(*a, *b) > 0); - if (!carry) { - ppc_avr_t tmp; - avr_qw_not(&tmp, *b); - avr_qw_add(&tmp, *a, tmp); - carry = ((tmp.VsrSD(0) == -1ull) && (tmp.VsrSD(1) == -1ull)); - } + Int128 tmp = int128_not(b->s128); + + r->VsrD(1) = int128_ult(int128_not(a->s128), tmp) || + int128_eq(int128_add(a->s128, tmp), int128_makes64(-1)); r->VsrD(0) = 0; - r->VsrD(1) = carry; -#endif } -void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +void helper_VSUBECUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { -#ifdef CONFIG_INT128 - r->u128 = - (~a->u128 < ~b->u128) || - ((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1)); -#else - int carry_in = c->VsrD(1) & 1; - int carry_out = (avr_qw_cmpu(*a, *b) > 0); - if (!carry_out && carry_in) { - ppc_avr_t tmp; - avr_qw_not(&tmp, *b); - avr_qw_add(&tmp, *a, tmp); - carry_out = ((tmp.VsrD(0) == -1ull) && (tmp.VsrD(1) == -1ull)); - } + Int128 tmp = int128_not(b->s128); + bool carry_out = int128_ult(int128_not(a->s128), tmp), + carry_in = int128_getlo(c->s128) & 1; + r->VsrD(1) = carry_out || (carry_in && int128_eq(int128_add(a->s128, tmp), + int128_makes64(-1))); r->VsrD(0) = 0; - r->VsrD(1) = carry_out; -#endif } #define BCD_PLUS_PREF_1 0xC diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 2add128cd1c..467f3046c8d 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -159,9 +159,6 @@ EXTRACT_HELPER(FPL, 25, 1); EXTRACT_HELPER(FPFLM, 17, 8); EXTRACT_HELPER(FPW, 16, 1); -/* mffscrni */ -EXTRACT_HELPER(RM, 11, 2); - /* addpcis */ EXTRACT_HELPER_SPLIT_3(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0) #if defined(TARGET_PPC64) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 6eed466f809..466d0d2f4c8 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -1877,6 +1877,12 @@ static int kvmppc_find_cpu_dt(char *buf, int buf_len) buf[0] = '\0'; while ((dirp = readdir(dp)) != NULL) { FILE *f; + + /* Don't accidentally read from the current and parent directories */ + if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) { + continue; + } + snprintf(buf, buf_len, "%s%s/clock-frequency", PROC_DEVTREE_CPU, dirp->d_name); f = fopen(buf, "r"); diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index f4985bae788..c8f69b3df9b 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -28,6 +28,11 @@ bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB; uint64_t pats = cpu->env.spr[SPR_PTCR] & PTCR_PATS; + /* Check if partition table is properly aligned */ + if (patb & MAKE_64BIT_MASK(0, pats + 12)) { + return false; + } + /* Calculate number of entries */ pats = 1ull << (pats + 12 - 4); if (pats <= lpid) { diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index d6d5ed8f8ed..674377a19e0 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -50,6 +50,21 @@ struct prtb_entry { #ifdef TARGET_PPC64 +/* + * tlbie[l] helper flags + * + * RIC, PRS, R and local are passed as flags in the last argument. + */ +#define TLBIE_F_RIC_SHIFT 0 +#define TLBIE_F_PRS_SHIFT 2 +#define TLBIE_F_R_SHIFT 3 +#define TLBIE_F_LOCAL_SHIFT 4 + +#define TLBIE_F_RIC_MASK (3 << TLBIE_F_RIC_SHIFT) +#define TLBIE_F_PRS (1 << TLBIE_F_PRS_SHIFT) +#define TLBIE_F_R (1 << TLBIE_F_R_SHIFT) +#define TLBIE_F_LOCAL (1 << TLBIE_F_LOCAL_SHIFT) + static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) { return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT); diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index da9fe99ff8b..b9b31fd2763 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -101,7 +101,7 @@ void dump_slb(PowerPCCPU *cpu) } #ifdef CONFIG_TCG -void helper_slbia(CPUPPCState *env, uint32_t ih) +void helper_SLBIA(CPUPPCState *env, uint32_t ih) { PowerPCCPU *cpu = env_archcpu(env); int starting_entry; @@ -173,6 +173,33 @@ void helper_slbia(CPUPPCState *env, uint32_t ih) } } +#if defined(TARGET_PPC64) +void helper_SLBIAG(CPUPPCState *env, target_ulong rs, uint32_t l) +{ + PowerPCCPU *cpu = env_archcpu(env); + int n; + + /* + * slbiag must always flush all TLB (which is equivalent to ERAT in ppc + * architecture). Matching on SLB_ESID_V is not good enough, because slbmte + * can overwrite a valid SLB without flushing its lookaside information. + * + * It would be possible to keep the TLB in synch with the SLB by flushing + * when a valid entry is overwritten by slbmte, and therefore slbiag would + * not have to flush unless it evicts a valid SLB entry. However it is + * expected that slbmte is more common than slbiag, and slbiag is usually + * going to evict valid SLB entries, so that tradeoff is unlikely to be a + * good one. + */ + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + + for (n = 0; n < cpu->hash64_opts->slb_size; n++) { + ppc_slb_t *slb = &env->slb[n]; + slb->esid &= ~SLB_ESID_V; + } +} +#endif + static void __helper_slbie(CPUPPCState *env, target_ulong addr, target_ulong global) { @@ -197,12 +224,12 @@ static void __helper_slbie(CPUPPCState *env, target_ulong addr, } } -void helper_slbie(CPUPPCState *env, target_ulong addr) +void helper_SLBIE(CPUPPCState *env, target_ulong addr) { __helper_slbie(env, addr, false); } -void helper_slbieg(CPUPPCState *env, target_ulong addr) +void helper_SLBIEG(CPUPPCState *env, target_ulong addr) { __helper_slbie(env, addr, true); } @@ -309,7 +336,7 @@ static int ppc_find_slb_vsid(PowerPCCPU *cpu, target_ulong rb, return 0; } -void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +void helper_SLBMTE(CPUPPCState *env, target_ulong rb, target_ulong rs) { PowerPCCPU *cpu = env_archcpu(env); @@ -319,7 +346,7 @@ void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) } } -target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) +target_ulong helper_SLBMFEE(CPUPPCState *env, target_ulong rb) { PowerPCCPU *cpu = env_archcpu(env); target_ulong rt = 0; @@ -331,7 +358,7 @@ target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) return rt; } -target_ulong helper_find_slb_vsid(CPUPPCState *env, target_ulong rb) +target_ulong helper_SLBFEE(CPUPPCState *env, target_ulong rb) { PowerPCCPU *cpu = env_archcpu(env); target_ulong rt = 0; @@ -343,7 +370,7 @@ target_ulong helper_find_slb_vsid(CPUPPCState *env, target_ulong rb) return rt; } -target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) +target_ulong helper_SLBMFEV(CPUPPCState *env, target_ulong rb) { PowerPCCPU *cpu = env_archcpu(env); target_ulong rt = 0; diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 21ac958e486..00f2e9fa2e6 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -236,16 +236,36 @@ static void ppc_radix64_set_rc(PowerPCCPU *cpu, MMUAccessType access_type, } } +static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls) +{ + /* + * Check if this is a valid level, according to POWER9 and POWER10 + * Processor User's Manuals, sections 4.10.4.1 and 5.10.6.1, respectively: + * Supported Radix Tree Configurations and Resulting Page Sizes. + * + * Note: these checks are specific to POWER9 and POWER10 CPUs. Any future + * CPUs that supports a different Radix MMU configuration will need their + * own implementation. + */ + switch (level) { + case 0: /* Root Page Dir */ + return psize == 52 && nls == 13; + case 1: + case 2: + return nls == 9; + case 3: + return nls == 9 || nls == 5; + default: + qemu_log_mask(LOG_GUEST_ERROR, "invalid radix level: %d\n", level); + return false; + } +} + static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr, uint64_t *pte_addr, uint64_t *nls, int *psize, uint64_t *pte, int *fault_cause) { - uint64_t index, pde; - - if (*nls < 5) { /* Directory maps less than 2**5 entries */ - *fault_cause |= DSISR_R_BADCONFIG; - return 1; - } + uint64_t index, mask, nlb, pde; /* Read page entry from guest address space */ pde = ldq_phys(as, *pte_addr); @@ -260,7 +280,17 @@ static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr, *nls = pde & R_PDE_NLS; index = eaddr >> (*psize - *nls); /* Shift */ index &= ((1UL << *nls) - 1); /* Mask */ - *pte_addr = (pde & R_PDE_NLB) + (index * sizeof(pde)); + nlb = pde & R_PDE_NLB; + mask = MAKE_64BIT_MASK(0, *nls + 3); + + if (nlb & mask) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: misaligned page dir/table base: 0x"TARGET_FMT_lx + " page dir size: 0x"TARGET_FMT_lx"\n", + __func__, nlb, mask + 1); + nlb &= ~mask; + } + *pte_addr = nlb + index * sizeof(pde); } return 0; } @@ -270,19 +300,30 @@ static int ppc_radix64_walk_tree(AddressSpace *as, vaddr eaddr, hwaddr *raddr, int *psize, uint64_t *pte, int *fault_cause, hwaddr *pte_addr) { - uint64_t index, pde, rpn , mask; + uint64_t index, pde, rpn, mask; + int level = 0; - if (nls < 5) { /* Directory maps less than 2**5 entries */ - *fault_cause |= DSISR_R_BADCONFIG; - return 1; + index = eaddr >> (*psize - nls); /* Shift */ + index &= ((1UL << nls) - 1); /* Mask */ + mask = MAKE_64BIT_MASK(0, nls + 3); + + if (base_addr & mask) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: misaligned page dir base: 0x"TARGET_FMT_lx + " page dir size: 0x"TARGET_FMT_lx"\n", + __func__, base_addr, mask + 1); + base_addr &= ~mask; } + *pte_addr = base_addr + index * sizeof(pde); - index = eaddr >> (*psize - nls); /* Shift */ - index &= ((1UL << nls) - 1); /* Mask */ - *pte_addr = base_addr + (index * sizeof(pde)); do { int ret; + if (!ppc_radix64_is_valid_level(level++, *psize, nls)) { + *fault_cause |= DSISR_R_BADCONFIG; + return 1; + } + ret = ppc_radix64_next_level(as, eaddr, pte_addr, &nls, psize, &pde, fault_cause); if (ret) { @@ -383,7 +424,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - uint64_t offset, size, prtbe_addr, prtbe0, base_addr, nls, index, pte; + uint64_t offset, size, prtb, prtbe_addr, prtbe0, base_addr, nls, index, pte; int fault_cause = 0, h_page_size, h_prot; hwaddr h_raddr, pte_addr; int ret; @@ -393,9 +434,18 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, __func__, access_str(access_type), eaddr, mmu_idx, pid); + prtb = (pate.dw1 & PATE1_R_PRTB); + size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12); + if (prtb & (size - 1)) { + /* Process Table not properly aligned */ + if (guest_visible) { + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_R_BADCONFIG); + } + return 1; + } + /* Index Process Table by PID to Find Corresponding Process Table Entry */ offset = pid * sizeof(struct prtb_entry); - size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12); if (offset >= size) { /* offset exceeds size of the process table */ if (guest_visible) { @@ -403,7 +453,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, } return 1; } - prtbe_addr = (pate.dw1 & PATE1_R_PRTB) + offset; + prtbe_addr = prtb + offset; if (vhyp_flat_addressing(cpu)) { prtbe0 = ldq_phys(cs->as, prtbe_addr); @@ -447,6 +497,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, } } else { uint64_t rpn, mask; + int level = 0; index = (eaddr & R_EADDR_MASK) >> (*g_page_size - nls); /* Shift */ index &= ((1UL << nls) - 1); /* Mask */ @@ -466,6 +517,11 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, return ret; } + if (!ppc_radix64_is_valid_level(level++, *g_page_size, nls)) { + fault_cause |= DSISR_R_BADCONFIG; + return 1; + } + ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK, &h_raddr, &nls, g_page_size, &pte, &fault_cause); if (ret) { @@ -568,7 +624,7 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, return false; } - /* Get Process Table */ + /* Get Partition Table */ if (cpu->vhyp) { PPCVirtualHypervisorClass *vhc; vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 15239dc95b5..2a91f3f46af 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -429,6 +429,160 @@ void helper_tlbie(CPUPPCState *env, target_ulong addr) ppc_tlb_invalidate_one(env, addr); } +#if defined(TARGET_PPC64) + +/* Invalidation Selector */ +#define TLBIE_IS_VA 0 +#define TLBIE_IS_PID 1 +#define TLBIE_IS_LPID 2 +#define TLBIE_IS_ALL 3 + +/* Radix Invalidation Control */ +#define TLBIE_RIC_TLB 0 +#define TLBIE_RIC_PWC 1 +#define TLBIE_RIC_ALL 2 +#define TLBIE_RIC_GRP 3 + +/* Radix Actual Page sizes */ +#define TLBIE_R_AP_4K 0 +#define TLBIE_R_AP_64K 5 +#define TLBIE_R_AP_2M 1 +#define TLBIE_R_AP_1G 2 + +/* RB field masks */ +#define TLBIE_RB_EPN_MASK PPC_BITMASK(0, 51) +#define TLBIE_RB_IS_MASK PPC_BITMASK(52, 53) +#define TLBIE_RB_AP_MASK PPC_BITMASK(56, 58) + +void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, + uint32_t flags) +{ + unsigned ric = (flags & TLBIE_F_RIC_MASK) >> TLBIE_F_RIC_SHIFT; + /* + * With the exception of the checks for invalid instruction forms, + * PRS is currently ignored, because we don't know if a given TLB entry + * is process or partition scoped. + */ + bool prs = flags & TLBIE_F_PRS; + bool r = flags & TLBIE_F_R; + bool local = flags & TLBIE_F_LOCAL; + bool effR; + unsigned is = extract64(rb, PPC_BIT_NR(53), 2); + unsigned ap; /* actual page size */ + target_ulong addr, pgoffs_mask; + + qemu_log_mask(CPU_LOG_MMU, + "%s: local=%d addr=" TARGET_FMT_lx " ric=%u prs=%d r=%d is=%u\n", + __func__, local, rb & TARGET_PAGE_MASK, ric, prs, r, is); + + effR = FIELD_EX64(env->msr, MSR, HV) ? r : env->spr[SPR_LPCR] & LPCR_HR; + + /* Partial TLB invalidation is supported for Radix only for now. */ + if (!effR) { + goto inval_all; + } + + /* Check for invalid instruction forms (effR=1). */ + if (unlikely(ric == TLBIE_RIC_GRP || + ((ric == TLBIE_RIC_PWC || ric == TLBIE_RIC_ALL) && + is == TLBIE_IS_VA) || + (!prs && is == TLBIE_IS_PID))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid instruction form: ric=%u prs=%d r=%d is=%u\n", + __func__, ric, prs, r, is); + goto invalid; + } + + /* We don't cache Page Walks. */ + if (ric == TLBIE_RIC_PWC) { + if (local) { + unsigned set = extract64(rb, PPC_BIT_NR(51), 12); + if (set != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid set: %d\n", + __func__, set); + goto invalid; + } + } + return; + } + + /* + * Invalidation by LPID or PID is not supported, so fallback + * to full TLB flush in these cases. + */ + if (is != TLBIE_IS_VA) { + goto inval_all; + } + + /* + * The results of an attempt to invalidate a translation outside of + * quadrant 0 for Radix Tree translation (effR=1, RIC=0, PRS=1, IS=0, + * and EA 0:1 != 0b00) are boundedly undefined. + */ + if (unlikely(ric == TLBIE_RIC_TLB && prs && is == TLBIE_IS_VA && + (rb & R_EADDR_QUADRANT) != R_EADDR_QUADRANT0)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: attempt to invalidate a translation outside of quadrant 0\n", + __func__); + goto inval_all; + } + + assert(is == TLBIE_IS_VA); + assert(ric == TLBIE_RIC_TLB || ric == TLBIE_RIC_ALL); + + ap = extract64(rb, PPC_BIT_NR(58), 3); + switch (ap) { + case TLBIE_R_AP_4K: + pgoffs_mask = 0xfffull; + break; + + case TLBIE_R_AP_64K: + pgoffs_mask = 0xffffull; + break; + + case TLBIE_R_AP_2M: + pgoffs_mask = 0x1fffffull; + break; + + case TLBIE_R_AP_1G: + pgoffs_mask = 0x3fffffffull; + break; + + default: + /* + * If the value specified in RS 0:31, RS 32:63, RB 54:55, RB 56:58, + * RB 44:51, or RB 56:63, when it is needed to perform the specified + * operation, is not supported by the implementation, the instruction + * is treated as if the instruction form were invalid. + */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid AP: %d\n", __func__, ap); + goto invalid; + } + + addr = rb & TLBIE_RB_EPN_MASK & ~pgoffs_mask; + + if (local) { + tlb_flush_page(env_cpu(env), addr); + } else { + tlb_flush_page_all_cpus(env_cpu(env), addr); + } + return; + +inval_all: + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + if (!local) { + env->tlb_need_flush |= TLB_NEED_GLOBAL_FLUSH; + } + return; + +invalid: + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); +} + +#endif + void helper_tlbiva(CPUPPCState *env, target_ulong addr) { /* tlbiva instruction only exists on BookE */ diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c index 0b805ef6e92..8250b1304e6 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/monitor.c @@ -55,6 +55,9 @@ static target_long monitor_get_decr(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); + if (!env->tb_env) { + return 0; + } return cpu_ppc_load_decr(env); } @@ -62,6 +65,9 @@ static target_long monitor_get_tbu(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); + if (!env->tb_env) { + return 0; + } return cpu_ppc_load_tbu(env); } @@ -69,6 +75,9 @@ static target_long monitor_get_tbl(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); + if (!env->tb_env) { + return 0; + } return cpu_ppc_load_tbl(env); } diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index 2bab6cece70..c3cc919ee41 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -22,7 +22,7 @@ static bool spr_groupA_read_allowed(DisasContext *ctx) { if (!ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); return false; } @@ -46,10 +46,10 @@ static bool spr_groupA_write_allowed(DisasContext *ctx) if (ctx->mmcr0_pmcc1) { /* PMCC = 0b01 */ - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); } else { /* PMCC = 0b00 */ - gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_hvpriv_exception(ctx, POWERPC_EXCP_PRIV_REG); } return false; @@ -214,7 +214,7 @@ void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) * Interrupt. */ if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); return; } @@ -249,7 +249,7 @@ void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) * Interrupt. */ if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); return; } diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 86d01d6e4e7..b80f56af7e4 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -143,7 +143,6 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) { store_booke_tsr(env, val); } -#endif /*****************************************************************************/ /* Embedded PowerPC specific helpers */ @@ -169,7 +168,7 @@ target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) (uint32_t)dcrn, (uint32_t)dcrn); raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | - POWERPC_EXCP_PRIV_REG, GETPC()); + POWERPC_EXCP_INVAL_INVAL, GETPC()); } } return val; @@ -192,7 +191,8 @@ void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val) (uint32_t)dcrn, (uint32_t)dcrn); raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | - POWERPC_EXCP_PRIV_REG, GETPC()); + POWERPC_EXCP_INVAL_INVAL, GETPC()); } } } +#endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 1d6daa4608c..5a18ee577f2 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -907,9 +907,9 @@ void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) } else if (sprn >= SPR_BOOKE_IVOR38 && sprn <= SPR_BOOKE_IVOR42) { sprn_offs = sprn - SPR_BOOKE_IVOR38 + 38; } else { - printf("Trying to write an unknown exception vector %d %03x\n", - sprn, sprn); - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + qemu_log_mask(LOG_GUEST_ERROR, "Trying to write an unknown exception" + " vector 0x%03x\n", sprn); + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } @@ -1267,38 +1267,43 @@ typedef struct opcode_t { const char *oname; } opcode_t; +static void gen_priv_opc(DisasContext *ctx) +{ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); +} + /* Helpers for priv. check */ -#define GEN_PRIV \ - do { \ - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; \ +#define GEN_PRIV(CTX) \ + do { \ + gen_priv_opc(CTX); return; \ } while (0) #if defined(CONFIG_USER_ONLY) -#define CHK_HV GEN_PRIV -#define CHK_SV GEN_PRIV -#define CHK_HVRM GEN_PRIV +#define CHK_HV(CTX) GEN_PRIV(CTX) +#define CHK_SV(CTX) GEN_PRIV(CTX) +#define CHK_HVRM(CTX) GEN_PRIV(CTX) #else -#define CHK_HV \ - do { \ - if (unlikely(ctx->pr || !ctx->hv)) { \ - GEN_PRIV; \ - } \ +#define CHK_HV(CTX) \ + do { \ + if (unlikely(ctx->pr || !ctx->hv)) {\ + GEN_PRIV(CTX); \ + } \ } while (0) -#define CHK_SV \ +#define CHK_SV(CTX) \ do { \ if (unlikely(ctx->pr)) { \ - GEN_PRIV; \ + GEN_PRIV(CTX); \ } \ } while (0) -#define CHK_HVRM \ - do { \ - if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) { \ - GEN_PRIV; \ - } \ +#define CHK_HVRM(CTX) \ + do { \ + if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) { \ + GEN_PRIV(CTX); \ + } \ } while (0) #endif -#define CHK_NONE +#define CHK_NONE(CTX) /*****************************************************************************/ /* PowerPC instructions table */ @@ -3252,7 +3257,7 @@ GEN_QEMU_STORE_64(st64r, BSWAP_MEMOP(MO_UQ)) static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ - chk; \ + chk(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ @@ -3270,7 +3275,7 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ static void glue(gen_, name##epx)(DisasContext *ctx) \ { \ TCGv EA; \ - CHK_SV; \ + CHK_SV(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ @@ -3298,7 +3303,7 @@ GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ - chk; \ + chk(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ @@ -3315,7 +3320,7 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ static void glue(gen_, name##epx)(DisasContext *ctx) \ { \ TCGv EA; \ - CHK_SV; \ + CHK_SV(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ @@ -4078,11 +4083,11 @@ static void gen_wait(DisasContext *ctx) static void gen_doze(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; + CHK_HV(ctx); t = tcg_const_i32(PPC_PM_DOZE); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); @@ -4094,11 +4099,11 @@ static void gen_doze(DisasContext *ctx) static void gen_nap(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; + CHK_HV(ctx); t = tcg_const_i32(PPC_PM_NAP); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); @@ -4110,11 +4115,11 @@ static void gen_nap(DisasContext *ctx) static void gen_stop(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; + CHK_HV(ctx); t = tcg_const_i32(PPC_PM_STOP); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); @@ -4126,11 +4131,11 @@ static void gen_stop(DisasContext *ctx) static void gen_sleep(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; + CHK_HV(ctx); t = tcg_const_i32(PPC_PM_SLEEP); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); @@ -4142,11 +4147,11 @@ static void gen_sleep(DisasContext *ctx) static void gen_rvwinkle(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; + CHK_HV(ctx); t = tcg_const_i32(PPC_PM_RVWINKLE); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); @@ -4476,7 +4481,7 @@ static void gen_mcrf(DisasContext *ctx) static void gen_rfi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* * This instruction doesn't exist anymore on 64-bit server @@ -4487,7 +4492,7 @@ static void gen_rfi(DisasContext *ctx) return; } /* Restore CPU state */ - CHK_SV; + CHK_SV(ctx); gen_icount_io_start(ctx); gen_update_cfar(ctx, ctx->cia); gen_helper_rfi(cpu_env); @@ -4499,10 +4504,10 @@ static void gen_rfi(DisasContext *ctx) static void gen_rfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* Restore CPU state */ - CHK_SV; + CHK_SV(ctx); gen_icount_io_start(ctx); gen_update_cfar(ctx, ctx->cia); gen_helper_rfid(cpu_env); @@ -4514,10 +4519,10 @@ static void gen_rfid(DisasContext *ctx) static void gen_rfscv(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* Restore CPU state */ - CHK_SV; + CHK_SV(ctx); gen_icount_io_start(ctx); gen_update_cfar(ctx, ctx->cia); gen_helper_rfscv(cpu_env); @@ -4529,10 +4534,10 @@ static void gen_rfscv(DisasContext *ctx) static void gen_hrfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* Restore CPU state */ - CHK_HV; + CHK_HV(ctx); gen_helper_hrfid(cpu_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -4733,7 +4738,7 @@ static void gen_mfcr(DisasContext *ctx) /* mfmsr */ static void gen_mfmsr(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr); } @@ -4789,11 +4794,11 @@ static inline void gen_op_mfspr(DisasContext *ctx) */ if (sprn & 0x10) { if (ctx->pr) { - gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_hvpriv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } } @@ -4847,7 +4852,7 @@ static void gen_mtmsrd(DisasContext *ctx) return; } - CHK_SV; + CHK_SV(ctx); #if !defined(CONFIG_USER_ONLY) TCGv t0, t1; @@ -4890,7 +4895,7 @@ static void gen_mtmsrd(DisasContext *ctx) static void gen_mtmsr(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); #if !defined(CONFIG_USER_ONLY) TCGv t0, t1; @@ -4976,11 +4981,11 @@ static void gen_mtspr(DisasContext *ctx) */ if (sprn & 0x10) { if (ctx->pr) { - gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { if (ctx->pr || sprn == 0) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_hvpriv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } } @@ -5022,7 +5027,7 @@ static void gen_dcbfep(DisasContext *ctx) { /* XXX: specification says this is treated as a load by the MMU */ TCGv t0; - CHK_SV; + CHK_SV(ctx); gen_set_access_type(ctx, ACCESS_CACHE); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); @@ -5034,11 +5039,11 @@ static void gen_dcbfep(DisasContext *ctx) static void gen_dcbi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv EA, val; - CHK_SV; + CHK_SV(ctx); EA = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); gen_addr_reg_index(ctx, EA); @@ -5223,11 +5228,11 @@ static void gen_dcba(DisasContext *ctx) static void gen_mfsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); @@ -5238,11 +5243,11 @@ static void gen_mfsr(DisasContext *ctx) static void gen_mfsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -5254,11 +5259,11 @@ static void gen_mfsrin(DisasContext *ctx) static void gen_mtsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); @@ -5269,10 +5274,10 @@ static void gen_mtsr(DisasContext *ctx) static void gen_mtsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); @@ -5288,11 +5293,11 @@ static void gen_mtsrin(DisasContext *ctx) static void gen_mfsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); @@ -5303,11 +5308,11 @@ static void gen_mfsr_64b(DisasContext *ctx) static void gen_mfsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -5319,11 +5324,11 @@ static void gen_mfsrin_64b(DisasContext *ctx) static void gen_mtsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); @@ -5334,11 +5339,11 @@ static void gen_mtsr_64b(DisasContext *ctx) static void gen_mtsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); @@ -5346,67 +5351,6 @@ static void gen_mtsrin_64b(DisasContext *ctx) #endif /* defined(CONFIG_USER_ONLY) */ } -/* slbmte */ -static void gen_slbmte(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_slbmfee(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, - cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_slbmfev(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, - cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_slbfee_(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); -#else - TCGLabel *l1, *l2; - - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } - gen_helper_find_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, - cpu_gpr[rB(ctx->opcode)]); - l1 = gen_new_label(); - l2 = gen_new_label(); - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0); - gen_set_label(l2); -#endif -} #endif /* defined(TARGET_PPC64) */ /*** Lookaside buffer management ***/ @@ -5416,83 +5360,25 @@ static void gen_slbfee_(DisasContext *ctx) static void gen_tlbia(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_HV; + CHK_HV(ctx); gen_helper_tlbia(cpu_env); #endif /* defined(CONFIG_USER_ONLY) */ } -/* tlbiel */ -static void gen_tlbiel(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - bool psr = (ctx->opcode >> 17) & 0x1; - - if (ctx->pr || (!ctx->hv && !psr && ctx->hr)) { - /* - * tlbiel is privileged except when PSR=0 and HR=1, making it - * hypervisor privileged. - */ - GEN_PRIV; - } - - gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* tlbie */ -static void gen_tlbie(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - bool psr = (ctx->opcode >> 17) & 0x1; - TCGv_i32 t1; - - if (ctx->pr) { - /* tlbie is privileged... */ - GEN_PRIV; - } else if (!ctx->hv) { - if (!ctx->gtse || (!psr && ctx->hr)) { - /* - * ... except when GTSE=0 or when PSR=0 and HR=1, making it - * hypervisor privileged. - */ - GEN_PRIV; - } - } - - if (NARROW_MODE(ctx)) { - TCGv t0 = tcg_temp_new(); - tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); - gen_helper_tlbie(cpu_env, t0); - tcg_temp_free(t0); - } else { - gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); - } - t1 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); - tcg_gen_ori_i32(t1, t1, TLB_NEED_GLOBAL_FLUSH); - tcg_gen_st_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); - tcg_temp_free_i32(t1); -#endif /* defined(CONFIG_USER_ONLY) */ -} - /* tlbsync */ static void gen_tlbsync(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else if (ctx->gtse) { - CHK_SV; /* If gtse is set then tlbsync is supervisor privileged */ + CHK_SV(ctx); /* If gtse is set then tlbsync is supervisor privileged */ } else { - CHK_HV; /* Else hypervisor privileged */ + CHK_HV(ctx); /* Else hypervisor privileged */ } /* BookS does both ptesync and tlbsync make tlbsync a nop for server */ @@ -5502,60 +5388,6 @@ static void gen_tlbsync(DisasContext *ctx) #endif /* defined(CONFIG_USER_ONLY) */ } -#if defined(TARGET_PPC64) -/* slbia */ -static void gen_slbia(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - uint32_t ih = (ctx->opcode >> 21) & 0x7; - TCGv_i32 t0 = tcg_const_i32(ih); - - CHK_SV; - - gen_helper_slbia(cpu_env, t0); - tcg_temp_free_i32(t0); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* slbie */ -static void gen_slbie(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* slbieg */ -static void gen_slbieg(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_slbieg(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* slbsync */ -static void gen_slbsync(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - gen_check_tlb_flush(ctx, true); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -#endif /* defined(TARGET_PPC64) */ - /*** External control ***/ /* Optional: */ @@ -5591,9 +5423,9 @@ static void gen_ecowx(DisasContext *ctx) static void gen_tlbld_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5602,9 +5434,9 @@ static void gen_tlbld_6xx(DisasContext *ctx) static void gen_tlbli_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5622,11 +5454,11 @@ static void gen_mfapidi(DisasContext *ctx) static void gen_tlbiva(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]); @@ -5853,11 +5685,11 @@ GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C); static void gen_mfdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv dcrn; - CHK_SV; + CHK_SV(ctx); dcrn = tcg_const_tl(SPR(ctx->opcode)); gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); tcg_temp_free(dcrn); @@ -5868,11 +5700,11 @@ static void gen_mfdcr(DisasContext *ctx) static void gen_mtdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv dcrn; - CHK_SV; + CHK_SV(ctx); dcrn = tcg_const_tl(SPR(ctx->opcode)); gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(dcrn); @@ -5884,9 +5716,9 @@ static void gen_mtdcr(DisasContext *ctx) static void gen_mfdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ @@ -5898,35 +5730,19 @@ static void gen_mfdcrx(DisasContext *ctx) static void gen_mtdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ #endif /* defined(CONFIG_USER_ONLY) */ } -/* mfdcrux (PPC 460) : user-mode access to DCR */ -static void gen_mfdcrux(DisasContext *ctx) -{ - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, - cpu_gpr[rA(ctx->opcode)]); - /* Note: Rc update flag set leads to undefined state of Rc0 */ -} - -/* mtdcrux (PPC 460) : user-mode access to DCR */ -static void gen_mtdcrux(DisasContext *ctx) -{ - gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)]); - /* Note: Rc update flag set leads to undefined state of Rc0 */ -} - /* dccci */ static void gen_dccci(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); /* interpreted as no-op */ } @@ -5934,11 +5750,11 @@ static void gen_dccci(DisasContext *ctx) static void gen_dcread(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv EA, val; - CHK_SV; + CHK_SV(ctx); gen_set_access_type(ctx, ACCESS_CACHE); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -5963,14 +5779,14 @@ static void gen_icbt_40x(DisasContext *ctx) /* iccci */ static void gen_iccci(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); /* interpreted as no-op */ } /* icread */ static void gen_icread(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); /* interpreted as no-op */ } @@ -5978,9 +5794,9 @@ static void gen_icread(DisasContext *ctx) static void gen_rfci_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ gen_helper_40x_rfci(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -5990,9 +5806,9 @@ static void gen_rfci_40x(DisasContext *ctx) static void gen_rfci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ gen_helper_rfci(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -6005,9 +5821,9 @@ static void gen_rfci(DisasContext *ctx) static void gen_rfdi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ gen_helper_rfdi(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -6018,9 +5834,9 @@ static void gen_rfdi(DisasContext *ctx) static void gen_rfmci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ gen_helper_rfmci(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -6033,9 +5849,9 @@ static void gen_rfmci(DisasContext *ctx) static void gen_tlbre_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env, @@ -6056,11 +5872,11 @@ static void gen_tlbre_40x(DisasContext *ctx) static void gen_tlbsx_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -6079,9 +5895,9 @@ static void gen_tlbsx_40x(DisasContext *ctx) static void gen_tlbwe_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: @@ -6105,9 +5921,9 @@ static void gen_tlbwe_40x(DisasContext *ctx) static void gen_tlbre_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: @@ -6131,11 +5947,11 @@ static void gen_tlbre_440(DisasContext *ctx) static void gen_tlbsx_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -6154,9 +5970,9 @@ static void gen_tlbsx_440(DisasContext *ctx) static void gen_tlbwe_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: case 1: @@ -6181,9 +5997,9 @@ static void gen_tlbwe_440(DisasContext *ctx) static void gen_tlbre_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_booke206_tlbre(cpu_env); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6192,11 +6008,11 @@ static void gen_tlbre_booke206(DisasContext *ctx) static void gen_tlbsx_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); if (rA(ctx->opcode)) { t0 = tcg_temp_new(); tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]); @@ -6214,9 +6030,9 @@ static void gen_tlbsx_booke206(DisasContext *ctx) static void gen_tlbwe_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_booke206_tlbwe(cpu_env); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6224,11 +6040,11 @@ static void gen_tlbwe_booke206(DisasContext *ctx) static void gen_tlbivax_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_booke206_tlbivax(cpu_env, t0); @@ -6239,11 +6055,11 @@ static void gen_tlbivax_booke206(DisasContext *ctx) static void gen_tlbilx_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); @@ -6271,11 +6087,11 @@ static void gen_tlbilx_booke206(DisasContext *ctx) static void gen_wrtee(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE)); tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); @@ -6293,9 +6109,9 @@ static void gen_wrtee(DisasContext *ctx) static void gen_wrteei(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); if (ctx->opcode & 0x00008000) { tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE)); /* Stop translation to have a chance to raise an exception */ @@ -6349,9 +6165,9 @@ static void gen_icbt_440(DisasContext *ctx) static void gen_msgclr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_HV; + CHK_HV(ctx); if (is_book3s_arch2x(ctx)) { gen_helper_book3s_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); } else { @@ -6363,9 +6179,9 @@ static void gen_msgclr(DisasContext *ctx) static void gen_msgsnd(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_HV; + CHK_HV(ctx); if (is_book3s_arch2x(ctx)) { gen_helper_book3s_msgsnd(cpu_gpr[rB(ctx->opcode)]); } else { @@ -6378,9 +6194,9 @@ static void gen_msgsnd(DisasContext *ctx) static void gen_msgclrp(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6388,9 +6204,9 @@ static void gen_msgclrp(DisasContext *ctx) static void gen_msgsndp(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6399,9 +6215,9 @@ static void gen_msgsndp(DisasContext *ctx) static void gen_msgsync(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_HV; + CHK_HV(ctx); #endif /* defined(CONFIG_USER_ONLY) */ /* interpreted as no-op */ } @@ -6512,7 +6328,7 @@ static void gen_tcheck(DisasContext *ctx) #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ + gen_priv_opc(ctx); \ } #else @@ -6520,7 +6336,7 @@ static inline void gen_##name(DisasContext *ctx) \ #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - CHK_SV; \ + CHK_SV(ctx); \ if (unlikely(!ctx->tm_enabled)) { \ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ return; \ @@ -6628,6 +6444,27 @@ static int times_16(DisasContext *ctx, int x) } \ } while (0) +#if !defined(CONFIG_USER_ONLY) +#define REQUIRE_SV(CTX) \ + do { \ + if (unlikely((CTX)->pr)) { \ + gen_priv_opc(CTX); \ + return true; \ + } \ + } while (0) + +#define REQUIRE_HV(CTX) \ + do { \ + if (unlikely((CTX)->pr || !(CTX)->hv)) \ + gen_priv_opc(CTX); \ + return true; \ + } \ + } while (0) +#else +#define REQUIRE_SV(CTX) do { gen_priv_opc(CTX); return true; } while (0) +#define REQUIRE_HV(CTX) do { gen_priv_opc(CTX); return true; } while (0) +#endif + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. @@ -6699,6 +6536,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/branch-impl.c.inc" +#include "translate/storage-ctrl-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { @@ -6927,27 +6766,13 @@ GEN_HANDLER2(mfsrin_64b, "mfsrin", 0x1F, 0x13, 0x14, 0x001F0001, GEN_HANDLER2(mtsr_64b, "mtsr", 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT_64B), GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbfee_, "slbfee.", 0x1F, 0x13, 0x1E, 0x001F0000, PPC_SEGMENT_64B), #endif GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), /* * XXX Those instructions will need to be handled differently for * different ISA versions */ -GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE), -GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE), -GEN_HANDLER_E(tlbiel, 0x1F, 0x12, 0x08, 0x00100001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(tlbie, 0x1F, 0x12, 0x09, 0x00100001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC), -#if defined(TARGET_PPC64) -GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x031FFC01, PPC_SLBI), -GEN_HANDLER(slbie, 0x1F, 0x12, 0x0D, 0x03FF0001, PPC_SLBI), -GEN_HANDLER_E(slbieg, 0x1F, 0x12, 0x0E, 0x001F0001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(slbsync, 0x1F, 0x12, 0x0A, 0x03FFF801, PPC_NONE, PPC2_ISA300), -#endif GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN), GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN), GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB), @@ -6958,8 +6783,6 @@ GEN_HANDLER(mfdcr, 0x1F, 0x03, 0x0A, 0x00000001, PPC_DCR), GEN_HANDLER(mtdcr, 0x1F, 0x03, 0x0E, 0x00000001, PPC_DCR), GEN_HANDLER(mfdcrx, 0x1F, 0x03, 0x08, 0x00000000, PPC_DCRX), GEN_HANDLER(mtdcrx, 0x1F, 0x03, 0x0C, 0x00000000, PPC_DCRX), -GEN_HANDLER(mfdcrux, 0x1F, 0x03, 0x09, 0x00000000, PPC_DCRUX), -GEN_HANDLER(mtdcrux, 0x1F, 0x03, 0x0D, 0x00000000, PPC_DCRUX), GEN_HANDLER(dccci, 0x1F, 0x06, 0x0E, 0x03E00001, PPC_4xx_COMMON), GEN_HANDLER(dcread, 0x1F, 0x06, 0x0F, 0x00000001, PPC_4xx_COMMON), GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT), diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 1aab32be036..db14d3bebc4 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -79,11 +79,8 @@ static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed) REQUIRE_INSNS_FLAGS(ctx, 64BX); if (!prefixed && !(ctx->insns_flags2 & PPC2_LSQ_ISA207)) { - if (ctx->pr) { - /* lq and stq were privileged prior to V. 2.07 */ - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return true; - } + /* lq and stq were privileged prior to V. 2.07 */ + REQUIRE_SV(ctx); if (ctx->le_mode) { gen_align_no_le(ctx); @@ -492,3 +489,54 @@ static bool trans_PEXTD(DisasContext *ctx, arg_X *a) #endif return true; } + +static bool trans_ADDG6S(DisasContext *ctx, arg_X *a) +{ + const uint64_t carry_bits = 0x1111111111111111ULL; + TCGv t0, t1, carry, zero = tcg_constant_tl(0); + + REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206); + + t0 = tcg_temp_new(); + t1 = tcg_const_tl(0); + carry = tcg_const_tl(0); + + for (int i = 0; i < 16; i++) { + tcg_gen_shri_tl(t0, cpu_gpr[a->ra], i * 4); + tcg_gen_andi_tl(t0, t0, 0xf); + tcg_gen_add_tl(t1, t1, t0); + + tcg_gen_shri_tl(t0, cpu_gpr[a->rb], i * 4); + tcg_gen_andi_tl(t0, t0, 0xf); + tcg_gen_add_tl(t1, t1, t0); + + tcg_gen_andi_tl(t1, t1, 0x10); + tcg_gen_setcond_tl(TCG_COND_NE, t1, t1, zero); + + tcg_gen_shli_tl(t0, t1, i * 4); + tcg_gen_or_tl(carry, carry, t0); + } + + tcg_gen_xori_tl(carry, carry, (target_long)carry_bits); + tcg_gen_muli_tl(cpu_gpr[a->rt], carry, 6); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(carry); + + return true; +} + +static bool trans_CDTBCD(DisasContext *ctx, arg_X_sa *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206); + gen_helper_CDTBCD(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} + +static bool trans_CBCDTD(DisasContext *ctx, arg_X_sa *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206); + gen_helper_CBCDTD(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index f9b58b844e9..0e893eafa70 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -615,141 +615,162 @@ static void gen_mcrfs(DisasContext *ctx) tcg_temp_free_i64(tnew_fpscr); } -/* mffs */ -static void gen_mffs(DisasContext *ctx) +static TCGv_i64 place_from_fpscr(int rt, uint64_t mask) { - TCGv_i64 t0; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); + TCGv_i64 fpscr = tcg_temp_new_i64(); + TCGv_i64 fpscr_masked = tcg_temp_new_i64(); + + tcg_gen_extu_tl_i64(fpscr, cpu_fpscr); + tcg_gen_andi_i64(fpscr_masked, fpscr, mask); + set_fpr(rt, fpscr_masked); + + tcg_temp_free_i64(fpscr_masked); + + return fpscr; +} + +static void store_fpscr_masked(TCGv_i64 fpscr, uint64_t clear_mask, + TCGv_i64 set_mask, uint32_t store_mask) +{ + TCGv_i64 fpscr_masked = tcg_temp_new_i64(); + TCGv_i32 st_mask = tcg_constant_i32(store_mask); + + tcg_gen_andi_i64(fpscr_masked, fpscr, ~clear_mask); + tcg_gen_or_i64(fpscr_masked, fpscr_masked, set_mask); + gen_helper_store_fpscr(cpu_env, fpscr_masked, st_mask); + + tcg_temp_free_i64(fpscr_masked); +} + +static bool trans_MFFS(DisasContext *ctx, arg_X_t_rc *a) +{ + TCGv_i64 fpscr; + + REQUIRE_FPU(ctx); + gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - set_fpr(rD(ctx->opcode), t0); - if (unlikely(Rc(ctx->opcode))) { + fpscr = place_from_fpscr(a->rt, UINT64_MAX); + if (a->rc) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); + + tcg_temp_free_i64(fpscr); + + return true; } -/* mffsl */ -static void gen_mffsl(DisasContext *ctx) +static bool trans_MFFSCE(DisasContext *ctx, arg_X_t *a) { - TCGv_i64 t0; + TCGv_i64 fpscr; - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_FPU(ctx); - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - /* Mask everything except mode, status, and enables. */ - tcg_gen_andi_i64(t0, t0, FP_DRN | FP_STATUS | FP_ENABLES | FP_RN); - set_fpr(rD(ctx->opcode), t0); - tcg_temp_free_i64(t0); + fpscr = place_from_fpscr(a->rt, UINT64_MAX); + store_fpscr_masked(fpscr, FP_ENABLES, tcg_constant_i64(0), 0x0003); + + tcg_temp_free_i64(fpscr); + + return true; } -/* mffsce */ -static void gen_mffsce(DisasContext *ctx) +static bool trans_MFFSCRN(DisasContext *ctx, arg_X_tb *a) { - TCGv_i64 t0; - TCGv_i32 mask; - - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } + TCGv_i64 t1, fpscr; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_FPU(ctx); - t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + get_fpr(t1, a->rb); + tcg_gen_andi_i64(t1, t1, FP_RN); gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - set_fpr(rD(ctx->opcode), t0); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_RN, t1, 0x0001); - /* Clear exception enable bits in the FPSCR. */ - tcg_gen_andi_i64(t0, t0, ~FP_ENABLES); - mask = tcg_const_i32(0x0003); - gen_helper_store_fpscr(cpu_env, t0, mask); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(fpscr); - tcg_temp_free_i32(mask); - tcg_temp_free_i64(t0); + return true; } -static void gen_helper_mffscrn(DisasContext *ctx, TCGv_i64 t1) +static bool trans_MFFSCDRN(DisasContext *ctx, arg_X_tb *a) { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i32 mask = tcg_const_i32(0x0001); + TCGv_i64 t1, fpscr; - gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - tcg_gen_andi_i64(t0, t0, FP_DRN | FP_ENABLES | FP_RN); - set_fpr(rD(ctx->opcode), t0); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_FPU(ctx); - /* Mask FPSCR value to clear RN. */ - tcg_gen_andi_i64(t0, t0, ~FP_RN); + t1 = tcg_temp_new_i64(); + get_fpr(t1, a->rb); + tcg_gen_andi_i64(t1, t1, FP_DRN); - /* Merge RN into FPSCR value. */ - tcg_gen_or_i64(t0, t0, t1); + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100); - gen_helper_store_fpscr(cpu_env, t0, mask); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(fpscr); - tcg_temp_free_i32(mask); - tcg_temp_free_i64(t0); + return true; } -/* mffscrn */ -static void gen_mffscrn(DisasContext *ctx) +static bool trans_MFFSCRNI(DisasContext *ctx, arg_X_imm2 *a) { - TCGv_i64 t1; - - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } + TCGv_i64 t1, fpscr; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_FPU(ctx); t1 = tcg_temp_new_i64(); - get_fpr(t1, rB(ctx->opcode)); - /* Mask FRB to get just RN. */ - tcg_gen_andi_i64(t1, t1, FP_RN); + tcg_gen_movi_i64(t1, a->imm); - gen_helper_mffscrn(ctx, t1); + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_RN, t1, 0x0001); tcg_temp_free_i64(t1); + tcg_temp_free_i64(fpscr); + + return true; } -/* mffscrni */ -static void gen_mffscrni(DisasContext *ctx) +static bool trans_MFFSCDRNI(DisasContext *ctx, arg_X_imm3 *a) { - TCGv_i64 t1; + TCGv_i64 t1, fpscr; - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } - - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_FPU(ctx); - t1 = tcg_const_i64((uint64_t)RM(ctx->opcode)); + t1 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t1, (uint64_t)a->imm << FPSCR_DRN0); - gen_helper_mffscrn(ctx, t1); + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100); tcg_temp_free_i64(t1); + tcg_temp_free_i64(fpscr); + + return true; +} + +static bool trans_MFFSL(DisasContext *ctx, arg_X_t *a) +{ + TCGv_i64 fpscr; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_FPU(ctx); + + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, + FP_DRN | FP_STATUS | FP_ENABLES | FP_NI | FP_RN); + + tcg_temp_free_i64(fpscr); + + return true; } /* mtfsb0 */ @@ -880,7 +901,7 @@ static void gen_lfdepx(DisasContext *ctx) { TCGv EA; TCGv_i64 t0; - CHK_SV; + CHK_SV(ctx); if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; @@ -1037,7 +1058,7 @@ static void gen_stfdepx(DisasContext *ctx) { TCGv EA; TCGv_i64 t0; - CHK_SV; + CHK_SV(ctx); if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index 0538ab2d2d0..1b65f5ab737 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -74,15 +74,6 @@ GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT), -GEN_HANDLER_E_2(mffs, 0x3F, 0x07, 0x12, 0x00, 0x00000000, PPC_FLOAT, PPC_NONE), -GEN_HANDLER_E_2(mffsce, 0x3F, 0x07, 0x12, 0x01, 0x00000000, PPC_FLOAT, - PPC2_ISA300), -GEN_HANDLER_E_2(mffsl, 0x3F, 0x07, 0x12, 0x18, 0x00000000, PPC_FLOAT, - PPC2_ISA300), -GEN_HANDLER_E_2(mffscrn, 0x3F, 0x07, 0x12, 0x16, 0x00000000, PPC_FLOAT, - PPC_NONE), -GEN_HANDLER_E_2(mffscrni, 0x3F, 0x07, 0x12, 0x17, 0x00000000, PPC_FLOAT, - PPC_NONE), GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x00000000, PPC_FLOAT), diff --git a/target/ppc/translate/storage-ctrl-impl.c.inc b/target/ppc/translate/storage-ctrl-impl.c.inc new file mode 100644 index 00000000000..6ea1d22ef99 --- /dev/null +++ b/target/ppc/translate/storage-ctrl-impl.c.inc @@ -0,0 +1,250 @@ +/* + * Power ISA decode for Storage Control instructions + * + * Copyright (c) 2022 Instituto de Pesquisas Eldorado (eldorado.org.br) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* + * Store Control Instructions + */ + +#include "mmu-book3s-v3.h" + +static bool trans_SLBIE(DisasContext *ctx, arg_SLBIE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SLBI); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIE(cpu_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBIEG(DisasContext *ctx, arg_SLBIEG *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIEG(cpu_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBIA(DisasContext *ctx, arg_SLBIA *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SLBI); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIA(cpu_env, tcg_constant_i32(a->ih)); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBIAG(DisasContext *ctx, arg_SLBIAG *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIAG(cpu_env, cpu_gpr[a->rs], tcg_constant_i32(a->l)); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBMTE(DisasContext *ctx, arg_SLBMTE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBMTE(cpu_env, cpu_gpr[a->rb], cpu_gpr[a->rt]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBMFEV(DisasContext *ctx, arg_SLBMFEV *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBMFEV(cpu_gpr[a->rt], cpu_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBMFEE(DisasContext *ctx, arg_SLBMFEE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBMFEE(cpu_gpr[a->rt], cpu_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBFEE(DisasContext *ctx, arg_SLBFEE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); +#else + +#if defined(TARGET_PPC64) + TCGLabel *l1, *l2; + + if (unlikely(ctx->pr)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return true; + } + gen_helper_SLBFEE(cpu_gpr[a->rt], cpu_env, + cpu_gpr[a->rb]); + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[a->rt], 0); + gen_set_label(l2); +#else + qemu_build_not_reached(); +#endif +#endif + return true; +} + +static bool trans_SLBSYNC(DisasContext *ctx, arg_SLBSYNC *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_check_tlb_flush(ctx, true); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) +{ +#if defined(CONFIG_USER_ONLY) + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; +#else + TCGv_i32 t1; + int rb; + + rb = a->rb; + + if ((ctx->insns_flags2 & PPC2_ISA300) == 0) { + /* + * Before Power ISA 3.0, the corresponding bits of RIC, PRS, and R + * (and RS for tlbiel) were reserved fields and should be ignored. + */ + a->ric = 0; + a->prs = false; + a->r = false; + if (local) { + a->rs = 0; + } + } + + if (ctx->pr) { + /* tlbie[l] is privileged... */ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; + } else if (!ctx->hv) { + if ((!a->prs && ctx->hr) || (!local && !ctx->gtse)) { + /* + * ... except when PRS=0 and HR=1, or when GTSE=0 for tlbie, + * making it hypervisor privileged. + */ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; + } + } + + if (!local && NARROW_MODE(ctx)) { + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[rb]); + gen_helper_tlbie(cpu_env, t0); + tcg_temp_free(t0); + +#if defined(TARGET_PPC64) + /* + * ISA 3.1B says that MSR SF must be 1 when this instruction is executed; + * otherwise the results are undefined. + */ + } else if (a->r) { + gen_helper_tlbie_isa300(cpu_env, cpu_gpr[rb], cpu_gpr[a->rs], + tcg_constant_i32(a->ric << TLBIE_F_RIC_SHIFT | + a->prs << TLBIE_F_PRS_SHIFT | + a->r << TLBIE_F_R_SHIFT | + local << TLBIE_F_LOCAL_SHIFT)); + return true; +#endif + + } else { + gen_helper_tlbie(cpu_env, cpu_gpr[rb]); + } + + if (local) { + return true; + } + + t1 = tcg_temp_new_i32(); + tcg_gen_ld_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_ori_i32(t1, t1, TLB_NEED_GLOBAL_FLUSH); + tcg_gen_st_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_temp_free_i32(t1); + + return true; +#endif +} + +TRANS_FLAGS(MEM_TLBIE, TLBIE, do_tlbie, false) +TRANS_FLAGS(MEM_TLBIE, TLBIEL, do_tlbie, true) diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index d7524c32046..e644ad3236e 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1234,18 +1234,6 @@ GEN_VXFORM_SAT(vsubuws, MO_32, sub, ussub, 0, 26); GEN_VXFORM_SAT(vsubsbs, MO_8, sub, sssub, 0, 28); GEN_VXFORM_SAT(vsubshs, MO_16, sub, sssub, 0, 29); GEN_VXFORM_SAT(vsubsws, MO_32, sub, sssub, 0, 30); -GEN_VXFORM(vadduqm, 0, 4); -GEN_VXFORM(vaddcuq, 0, 5); -GEN_VXFORM3(vaddeuqm, 30, 0); -GEN_VXFORM3(vaddecuq, 30, 0); -GEN_VXFORM_DUAL(vaddeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ - vaddecuq, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM(vsubuqm, 0, 20); -GEN_VXFORM(vsubcuq, 0, 21); -GEN_VXFORM3(vsubeuqm, 31, 0); -GEN_VXFORM3(vsubecuq, 31, 0); -GEN_VXFORM_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ - vsubecuq, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM_TRANS(vsl, 2, 7); GEN_VXFORM_TRANS(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); @@ -2572,6 +2560,12 @@ static bool do_va_helper(DisasContext *ctx, arg_VA *a, return true; } +TRANS_FLAGS2(ALTIVEC_207, VADDECUQ, do_va_helper, gen_helper_VADDECUQ) +TRANS_FLAGS2(ALTIVEC_207, VADDEUQM, do_va_helper, gen_helper_VADDEUQM) + +TRANS_FLAGS2(ALTIVEC_207, VSUBEUQM, do_va_helper, gen_helper_VSUBEUQM) +TRANS_FLAGS2(ALTIVEC_207, VSUBECUQ, do_va_helper, gen_helper_VSUBECUQ) + TRANS_FLAGS(ALTIVEC, VPERM, do_va_helper, gen_helper_VPERM) TRANS_FLAGS2(ISA300, VPERMR, do_va_helper, gen_helper_VPERMR) @@ -2717,7 +2711,6 @@ GEN_VXFORM_TRANS(vgbbd, 6, 20); GEN_VXFORM(vpmsumb, 4, 16) GEN_VXFORM(vpmsumh, 4, 17) GEN_VXFORM(vpmsumw, 4, 18) -GEN_VXFORM(vpmsumd, 4, 19) #define GEN_BCD(op) \ static void gen_##op(DisasContext *ctx) \ @@ -2862,11 +2855,6 @@ GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \ bcdus, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \ bcdtrunc, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_DUAL(vsubuqm, PPC2_ALTIVEC_207, PPC_NONE, \ - bcdtrunc, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_DUAL(vsubcuq, PPC2_ALTIVEC_207, PPC_NONE, \ - bcdutrunc, PPC_NONE, PPC2_ISA300) - static void gen_vsbox(DisasContext *ctx) { @@ -3101,6 +3089,14 @@ static bool do_vx_helper(DisasContext *ctx, arg_VX *a, return true; } +TRANS_FLAGS2(ALTIVEC_207, VADDCUQ, do_vx_helper, gen_helper_VADDCUQ) +TRANS_FLAGS2(ALTIVEC_207, VADDUQM, do_vx_helper, gen_helper_VADDUQM) + +TRANS_FLAGS2(ALTIVEC_207, VPMSUMD, do_vx_helper, gen_helper_VPMSUMD) + +TRANS_FLAGS2(ALTIVEC_207, VSUBCUQ, do_vx_helper, gen_helper_VSUBCUQ) +TRANS_FLAGS2(ALTIVEC_207, VSUBUQM, do_vx_helper, gen_helper_VSUBUQM) + static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -3238,6 +3234,157 @@ TRANS(VMULHSD, do_vx_mulh, true , do_vx_vmulhd_i64) TRANS(VMULHUW, do_vx_mulh, false, do_vx_vmulhw_i64) TRANS(VMULHUD, do_vx_mulh, false, do_vx_vmulhd_i64) +static bool do_vdiv_vmod(DisasContext *ctx, arg_VX *a, const int vece, + void (*func_32)(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b), + void (*func_64)(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b)) +{ + const GVecGen3 op = { + .fni4 = func_32, + .fni8 = func_64, + .vece = vece + }; + + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &op); + + return true; +} + +#define DIVU32(NAME, DIV) \ +static void NAME(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) \ +{ \ + TCGv_i32 zero = tcg_constant_i32(0); \ + TCGv_i32 one = tcg_constant_i32(1); \ + tcg_gen_movcond_i32(TCG_COND_EQ, b, b, zero, one, b); \ + DIV(t, a, b); \ +} + +#define DIVS32(NAME, DIV) \ +static void NAME(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) \ +{ \ + TCGv_i32 t0 = tcg_temp_new_i32(); \ + TCGv_i32 t1 = tcg_temp_new_i32(); \ + tcg_gen_setcondi_i32(TCG_COND_EQ, t0, a, INT32_MIN); \ + tcg_gen_setcondi_i32(TCG_COND_EQ, t1, b, -1); \ + tcg_gen_and_i32(t0, t0, t1); \ + tcg_gen_setcondi_i32(TCG_COND_EQ, t1, b, 0); \ + tcg_gen_or_i32(t0, t0, t1); \ + tcg_gen_movi_i32(t1, 0); \ + tcg_gen_movcond_i32(TCG_COND_NE, b, t0, t1, t0, b); \ + DIV(t, a, b); \ + tcg_temp_free_i32(t0); \ + tcg_temp_free_i32(t1); \ +} + +#define DIVU64(NAME, DIV) \ +static void NAME(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) \ +{ \ + TCGv_i64 zero = tcg_constant_i64(0); \ + TCGv_i64 one = tcg_constant_i64(1); \ + tcg_gen_movcond_i64(TCG_COND_EQ, b, b, zero, one, b); \ + DIV(t, a, b); \ +} + +#define DIVS64(NAME, DIV) \ +static void NAME(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i64 t1 = tcg_temp_new_i64(); \ + tcg_gen_setcondi_i64(TCG_COND_EQ, t0, a, INT64_MIN); \ + tcg_gen_setcondi_i64(TCG_COND_EQ, t1, b, -1); \ + tcg_gen_and_i64(t0, t0, t1); \ + tcg_gen_setcondi_i64(TCG_COND_EQ, t1, b, 0); \ + tcg_gen_or_i64(t0, t0, t1); \ + tcg_gen_movi_i64(t1, 0); \ + tcg_gen_movcond_i64(TCG_COND_NE, b, t0, t1, t0, b); \ + DIV(t, a, b); \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ +} + +DIVS32(do_divsw, tcg_gen_div_i32) +DIVU32(do_divuw, tcg_gen_divu_i32) +DIVS64(do_divsd, tcg_gen_div_i64) +DIVU64(do_divud, tcg_gen_divu_i64) + +TRANS_FLAGS2(ISA310, VDIVSW, do_vdiv_vmod, MO_32, do_divsw, NULL) +TRANS_FLAGS2(ISA310, VDIVUW, do_vdiv_vmod, MO_32, do_divuw, NULL) +TRANS_FLAGS2(ISA310, VDIVSD, do_vdiv_vmod, MO_64, NULL, do_divsd) +TRANS_FLAGS2(ISA310, VDIVUD, do_vdiv_vmod, MO_64, NULL, do_divud) +TRANS_FLAGS2(ISA310, VDIVSQ, do_vx_helper, gen_helper_VDIVSQ) +TRANS_FLAGS2(ISA310, VDIVUQ, do_vx_helper, gen_helper_VDIVUQ) + +static void do_dives_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i64 val1, val2; + + val1 = tcg_temp_new_i64(); + val2 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(val1, a); + tcg_gen_ext_i32_i64(val2, b); + + /* (a << 32)/b */ + tcg_gen_shli_i64(val1, val1, 32); + tcg_gen_div_i64(val1, val1, val2); + + /* if quotient doesn't fit in 32 bits the result is undefined */ + tcg_gen_extrl_i64_i32(t, val1); + + tcg_temp_free_i64(val1); + tcg_temp_free_i64(val2); +} + +static void do_diveu_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i64 val1, val2; + + val1 = tcg_temp_new_i64(); + val2 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(val1, a); + tcg_gen_extu_i32_i64(val2, b); + + /* (a << 32)/b */ + tcg_gen_shli_i64(val1, val1, 32); + tcg_gen_divu_i64(val1, val1, val2); + + /* if quotient doesn't fit in 32 bits the result is undefined */ + tcg_gen_extrl_i64_i32(t, val1); + + tcg_temp_free_i64(val1); + tcg_temp_free_i64(val2); +} + +DIVS32(do_divesw, do_dives_i32) +DIVU32(do_diveuw, do_diveu_i32) + +DIVS32(do_modsw, tcg_gen_rem_i32) +DIVU32(do_moduw, tcg_gen_remu_i32) +DIVS64(do_modsd, tcg_gen_rem_i64) +DIVU64(do_modud, tcg_gen_remu_i64) + +TRANS_FLAGS2(ISA310, VDIVESW, do_vdiv_vmod, MO_32, do_divesw, NULL) +TRANS_FLAGS2(ISA310, VDIVEUW, do_vdiv_vmod, MO_32, do_diveuw, NULL) +TRANS_FLAGS2(ISA310, VDIVESD, do_vx_helper, gen_helper_VDIVESD) +TRANS_FLAGS2(ISA310, VDIVEUD, do_vx_helper, gen_helper_VDIVEUD) +TRANS_FLAGS2(ISA310, VDIVESQ, do_vx_helper, gen_helper_VDIVESQ) +TRANS_FLAGS2(ISA310, VDIVEUQ, do_vx_helper, gen_helper_VDIVEUQ) + +TRANS_FLAGS2(ISA310, VMODSW, do_vdiv_vmod, MO_32, do_modsw , NULL) +TRANS_FLAGS2(ISA310, VMODUW, do_vdiv_vmod, MO_32, do_moduw, NULL) +TRANS_FLAGS2(ISA310, VMODSD, do_vdiv_vmod, MO_64, NULL, do_modsd) +TRANS_FLAGS2(ISA310, VMODUD, do_vdiv_vmod, MO_64, NULL, do_modud) +TRANS_FLAGS2(ISA310, VMODSQ, do_vx_helper, gen_helper_VMODSQ) +TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) + +#undef DIVS32 +#undef DIVU32 +#undef DIVS64 +#undef DIVU64 + #undef GEN_VR_LDX #undef GEN_VR_STX #undef GEN_VR_LVE diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index d7cc57868ec..a3a0fd06500 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -126,12 +126,8 @@ GEN_VXFORM(vsubuws, 0, 26), GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_ALTIVEC, PPC2_ISA300), GEN_VXFORM(vsubshs, 0, 29), GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_207(vadduqm, 0, 4), -GEN_VXFORM_207(vaddcuq, 0, 5), -GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), -GEN_VXFORM_DUAL(vsubuqm, bcdtrunc, 0, 20, PPC2_ALTIVEC_207, PPC2_ISA300), -GEN_VXFORM_DUAL(vsubcuq, bcdutrunc, 0, 21, PPC2_ALTIVEC_207, PPC2_ISA300), -GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_300(bcdtrunc, 0, 20), +GEN_VXFORM_300(bcdutrunc, 0, 21), GEN_VXFORM(vsl, 2, 7), GEN_VXFORM(vsr, 2, 11), GEN_VXFORM(vpkuhum, 7, 0), @@ -237,7 +233,6 @@ GEN_VXFORM_207(vgbbd, 6, 20), GEN_VXFORM_207(vpmsumb, 4, 16), GEN_VXFORM_207(vpmsumh, 4, 17), GEN_VXFORM_207(vpmsumw, 4, 18), -GEN_VXFORM_207(vpmsumd, 4, 19), GEN_VXFORM_207(vsbox, 4, 23), diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.h new file mode 100644 index 00000000000..7c8a59e0cc3 --- /dev/null +++ b/target/riscv/common-semi-target.h @@ -0,0 +1,50 @@ +/* + * Target-specific parts of semihosting/arm-compat-semi.c. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019, 2022 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_RISCV_COMMON_SEMI_TARGET_H +#define TARGET_RISCV_COMMON_SEMI_TARGET_H + +static inline target_ulong common_semi_arg(CPUState *cs, int argno) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xA0 + argno]; +} + +static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + env->gpr[xA0] = ret; +} + +static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +{ + return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); +} + +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return riscv_cpu_mxl(env) != MXL_RV32; +} + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xSP]; +} + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + return true; +} + +#endif diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 05e65213517..1bb3973806d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -173,6 +173,8 @@ static void rv64_base_cpu_init(Object *obj) /* We set this in the realise function */ set_misa(env, MXL_RV64, 0); register_cpu_props(DEVICE(obj)); + /* Set latest version of privileged specification */ + set_priv_version(env, PRIV_VERSION_1_12_0); } static void rv64_sifive_u_cpu_init(Object *obj) @@ -204,6 +206,8 @@ static void rv128_base_cpu_init(Object *obj) /* We set this in the realise function */ set_misa(env, MXL_RV128, 0); register_cpu_props(DEVICE(obj)); + /* Set latest version of privileged specification */ + set_priv_version(env, PRIV_VERSION_1_12_0); } #else static void rv32_base_cpu_init(Object *obj) @@ -212,6 +216,8 @@ static void rv32_base_cpu_init(Object *obj) /* We set this in the realise function */ set_misa(env, MXL_RV32, 0); register_cpu_props(DEVICE(obj)); + /* Set latest version of privileged specification */ + set_priv_version(env, PRIV_VERSION_1_12_0); } static void rv32_sifive_u_cpu_init(Object *obj) @@ -237,7 +243,7 @@ static void rv32_ibex_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + set_priv_version(env, PRIV_VERSION_1_11_0); cpu->cfg.mmu = false; cpu->cfg.epmp = true; } @@ -524,7 +530,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); CPUClass *cc = CPU_CLASS(mcc); - int priv_version = 0; + int priv_version = -1; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -548,10 +554,8 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } } - if (priv_version) { + if (priv_version >= PRIV_VERSION_1_10_0) { set_priv_version(env, priv_version); - } else if (!env->priv_ver) { - set_priv_version(env, PRIV_VERSION_1_12_0); } if (cpu->cfg.mmu) { @@ -851,7 +855,6 @@ static void riscv_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); - cpu->cfg.ext_counters = true; cpu->cfg.ext_ifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.mmu = true; @@ -879,7 +882,7 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), DEFINE_PROP_BOOL("v", RISCVCPU, cfg.ext_v, false), DEFINE_PROP_BOOL("h", RISCVCPU, cfg.ext_h, true), - DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), + DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7d6397acdfb..5c7acc055ac 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -117,6 +117,8 @@ typedef struct CPUArchState CPURISCVState; #endif #define RV_VLEN_MAX 1024 +#define RV_MAX_MHPMEVENTS 32 +#define RV_MAX_MHPMCOUNTERS 32 FIELD(VTYPE, VLMUL, 0, 3) FIELD(VTYPE, VSEW, 3, 3) @@ -125,6 +127,18 @@ FIELD(VTYPE, VMA, 7, 1) FIELD(VTYPE, VEDIV, 8, 2) FIELD(VTYPE, RESERVED, 10, sizeof(target_ulong) * 8 - 11) +typedef struct PMUCTRState { + /* Current value of a counter */ + target_ulong mhpmcounter_val; + /* Current value of a counter in RV32*/ + target_ulong mhpmcounterh_val; + /* Snapshot values of counter */ + target_ulong mhpmcounter_prev; + /* Snapshort value of a counter in RV32 */ + target_ulong mhpmcounterh_prev; + bool started; +} PMUCTRState; + struct CPUArchState { target_ulong gpr[32]; target_ulong gprh[32]; /* 64 top bits of the 128-bit registers */ @@ -275,6 +289,14 @@ struct CPUArchState { target_ulong scounteren; target_ulong mcounteren; + target_ulong mcountinhibit; + + /* PMU counter state */ + PMUCTRState pmu_ctrs[RV_MAX_MHPMCOUNTERS]; + + /* PMU event selector configured values. First three are unused*/ + target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; + target_ulong sscratch; target_ulong mscratch; @@ -397,7 +419,6 @@ struct RISCVCPUConfig { bool ext_zksed; bool ext_zksh; bool ext_zkt; - bool ext_counters; bool ext_ifencei; bool ext_icsr; bool ext_svinval; @@ -421,6 +442,7 @@ struct RISCVCPUConfig { /* Vendor-specific custom extensions */ bool ext_XVentanaCondOps; + uint8_t pmu_num; char *priv_spec; char *user_spec; char *bext_spec; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 4d04b20d064..6be5a9e9f04 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -174,14 +174,8 @@ #define CSR_MIREG 0x351 /* Machine-Level Interrupts (AIA) */ -#define CSR_MTOPI 0xfb0 - -/* Machine-Level IMSIC Interface (AIA) */ -#define CSR_MSETEIPNUM 0x358 -#define CSR_MCLREIPNUM 0x359 -#define CSR_MSETEIENUM 0x35a -#define CSR_MCLREIENUM 0x35b #define CSR_MTOPEI 0x35c +#define CSR_MTOPI 0xfb0 /* Virtual Interrupts for Supervisor Level (AIA) */ #define CSR_MVIEN 0x308 @@ -221,14 +215,8 @@ #define CSR_SIREG 0x151 /* Supervisor-Level Interrupts (AIA) */ -#define CSR_STOPI 0xdb0 - -/* Supervisor-Level IMSIC Interface (AIA) */ -#define CSR_SSETEIPNUM 0x158 -#define CSR_SCLREIPNUM 0x159 -#define CSR_SSETEIENUM 0x15a -#define CSR_SCLREIENUM 0x15b #define CSR_STOPEI 0x15c +#define CSR_STOPI 0xdb0 /* Supervisor-Level High-Half CSRs (AIA) */ #define CSR_SIEH 0x114 @@ -279,14 +267,8 @@ #define CSR_VSIREG 0x251 /* VS-Level Interrupts (H-extension with AIA) */ -#define CSR_VSTOPI 0xeb0 - -/* VS-Level IMSIC Interface (H-extension with AIA) */ -#define CSR_VSSETEIPNUM 0x258 -#define CSR_VSCLREIPNUM 0x259 -#define CSR_VSSETEIENUM 0x25a -#define CSR_VSCLREIENUM 0x25b #define CSR_VSTOPEI 0x25c +#define CSR_VSTOPI 0xeb0 /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ #define CSR_HIDELEGH 0x613 @@ -367,6 +349,10 @@ #define CSR_MHPMCOUNTER29 0xb1d #define CSR_MHPMCOUNTER30 0xb1e #define CSR_MHPMCOUNTER31 0xb1f + +/* Machine counter-inhibit register */ +#define CSR_MCOUNTINHIBIT 0x320 + #define CSR_MHPMEVENT3 0x323 #define CSR_MHPMEVENT4 0x324 #define CSR_MHPMEVENT5 0x325 @@ -788,7 +774,7 @@ typedef enum RISCVException { #define IPRIO_IRQ_BITS 8 #define IPRIO_MMAXIPRIO 255 #define IPRIO_DEFAULT_UPPER 4 -#define IPRIO_DEFAULT_MIDDLE (IPRIO_DEFAULT_UPPER + 24) +#define IPRIO_DEFAULT_MIDDLE (IPRIO_DEFAULT_UPPER + 12) #define IPRIO_DEFAULT_M IPRIO_DEFAULT_MIDDLE #define IPRIO_DEFAULT_S (IPRIO_DEFAULT_M + 3) #define IPRIO_DEFAULT_SGEXT (IPRIO_DEFAULT_S + 3) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 4a6700c8908..59b3680b1b2 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -168,17 +168,17 @@ void riscv_cpu_update_mask(CPURISCVState *env) * 14 " * 15 " * 16 " - * 18 Debug/trace interrupt - * 20 (Reserved interrupt) + * 17 " + * 18 " + * 19 " + * 20 " + * 21 " * 22 " - * 24 " - * 26 " - * 28 " - * 30 (Reserved for standard reporting of bus or system errors) + * 23 " */ static const int hviprio_index2irq[] = { - 0, 1, 4, 5, 8, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30 }; + 0, 1, 4, 5, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; static const int hviprio_index2rdzero[] = { 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -207,50 +207,60 @@ int riscv_cpu_hviprio_index2irq(int index, int *out_irq, int *out_rdzero) * Default | * Priority | Major Interrupt Numbers * ---------------------------------------------------------------- - * Highest | 63 (3f), 62 (3e), 31 (1f), 30 (1e), 61 (3d), 60 (3c), - * | 59 (3b), 58 (3a), 29 (1d), 28 (1c), 57 (39), 56 (38), - * | 55 (37), 54 (36), 27 (1b), 26 (1a), 53 (35), 52 (34), - * | 51 (33), 50 (32), 25 (19), 24 (18), 49 (31), 48 (30) + * Highest | 47, 23, 46, 45, 22, 44, + * | 43, 21, 42, 41, 20, 40 * | * | 11 (0b), 3 (03), 7 (07) * | 9 (09), 1 (01), 5 (05) * | 12 (0c) * | 10 (0a), 2 (02), 6 (06) * | - * | 47 (2f), 46 (2e), 23 (17), 22 (16), 45 (2d), 44 (2c), - * | 43 (2b), 42 (2a), 21 (15), 20 (14), 41 (29), 40 (28), - * | 39 (27), 38 (26), 19 (13), 18 (12), 37 (25), 36 (24), - * Lowest | 35 (23), 34 (22), 17 (11), 16 (10), 33 (21), 32 (20) + * | 39, 19, 38, 37, 18, 36, + * Lowest | 35, 17, 34, 33, 16, 32 * ---------------------------------------------------------------- */ static const uint8_t default_iprio[64] = { - [63] = IPRIO_DEFAULT_UPPER, - [62] = IPRIO_DEFAULT_UPPER + 1, - [31] = IPRIO_DEFAULT_UPPER + 2, - [30] = IPRIO_DEFAULT_UPPER + 3, - [61] = IPRIO_DEFAULT_UPPER + 4, - [60] = IPRIO_DEFAULT_UPPER + 5, - - [59] = IPRIO_DEFAULT_UPPER + 6, - [58] = IPRIO_DEFAULT_UPPER + 7, - [29] = IPRIO_DEFAULT_UPPER + 8, - [28] = IPRIO_DEFAULT_UPPER + 9, - [57] = IPRIO_DEFAULT_UPPER + 10, - [56] = IPRIO_DEFAULT_UPPER + 11, - - [55] = IPRIO_DEFAULT_UPPER + 12, - [54] = IPRIO_DEFAULT_UPPER + 13, - [27] = IPRIO_DEFAULT_UPPER + 14, - [26] = IPRIO_DEFAULT_UPPER + 15, - [53] = IPRIO_DEFAULT_UPPER + 16, - [52] = IPRIO_DEFAULT_UPPER + 17, - - [51] = IPRIO_DEFAULT_UPPER + 18, - [50] = IPRIO_DEFAULT_UPPER + 19, - [25] = IPRIO_DEFAULT_UPPER + 20, - [24] = IPRIO_DEFAULT_UPPER + 21, - [49] = IPRIO_DEFAULT_UPPER + 22, - [48] = IPRIO_DEFAULT_UPPER + 23, + /* Custom interrupts 48 to 63 */ + [63] = IPRIO_MMAXIPRIO, + [62] = IPRIO_MMAXIPRIO, + [61] = IPRIO_MMAXIPRIO, + [60] = IPRIO_MMAXIPRIO, + [59] = IPRIO_MMAXIPRIO, + [58] = IPRIO_MMAXIPRIO, + [57] = IPRIO_MMAXIPRIO, + [56] = IPRIO_MMAXIPRIO, + [55] = IPRIO_MMAXIPRIO, + [54] = IPRIO_MMAXIPRIO, + [53] = IPRIO_MMAXIPRIO, + [52] = IPRIO_MMAXIPRIO, + [51] = IPRIO_MMAXIPRIO, + [50] = IPRIO_MMAXIPRIO, + [49] = IPRIO_MMAXIPRIO, + [48] = IPRIO_MMAXIPRIO, + + /* Custom interrupts 24 to 31 */ + [31] = IPRIO_MMAXIPRIO, + [30] = IPRIO_MMAXIPRIO, + [29] = IPRIO_MMAXIPRIO, + [28] = IPRIO_MMAXIPRIO, + [27] = IPRIO_MMAXIPRIO, + [26] = IPRIO_MMAXIPRIO, + [25] = IPRIO_MMAXIPRIO, + [24] = IPRIO_MMAXIPRIO, + + [47] = IPRIO_DEFAULT_UPPER, + [23] = IPRIO_DEFAULT_UPPER + 1, + [46] = IPRIO_DEFAULT_UPPER + 2, + [45] = IPRIO_DEFAULT_UPPER + 3, + [22] = IPRIO_DEFAULT_UPPER + 4, + [44] = IPRIO_DEFAULT_UPPER + 5, + + [43] = IPRIO_DEFAULT_UPPER + 6, + [21] = IPRIO_DEFAULT_UPPER + 7, + [42] = IPRIO_DEFAULT_UPPER + 8, + [41] = IPRIO_DEFAULT_UPPER + 9, + [20] = IPRIO_DEFAULT_UPPER + 10, + [40] = IPRIO_DEFAULT_UPPER + 11, [11] = IPRIO_DEFAULT_M, [3] = IPRIO_DEFAULT_M + 1, @@ -266,33 +276,19 @@ static const uint8_t default_iprio[64] = { [2] = IPRIO_DEFAULT_VS + 1, [6] = IPRIO_DEFAULT_VS + 2, - [47] = IPRIO_DEFAULT_LOWER, - [46] = IPRIO_DEFAULT_LOWER + 1, - [23] = IPRIO_DEFAULT_LOWER + 2, - [22] = IPRIO_DEFAULT_LOWER + 3, - [45] = IPRIO_DEFAULT_LOWER + 4, - [44] = IPRIO_DEFAULT_LOWER + 5, - - [43] = IPRIO_DEFAULT_LOWER + 6, - [42] = IPRIO_DEFAULT_LOWER + 7, - [21] = IPRIO_DEFAULT_LOWER + 8, - [20] = IPRIO_DEFAULT_LOWER + 9, - [41] = IPRIO_DEFAULT_LOWER + 10, - [40] = IPRIO_DEFAULT_LOWER + 11, - - [39] = IPRIO_DEFAULT_LOWER + 12, - [38] = IPRIO_DEFAULT_LOWER + 13, - [19] = IPRIO_DEFAULT_LOWER + 14, - [18] = IPRIO_DEFAULT_LOWER + 15, - [37] = IPRIO_DEFAULT_LOWER + 16, - [36] = IPRIO_DEFAULT_LOWER + 17, - - [35] = IPRIO_DEFAULT_LOWER + 18, - [34] = IPRIO_DEFAULT_LOWER + 19, - [17] = IPRIO_DEFAULT_LOWER + 20, - [16] = IPRIO_DEFAULT_LOWER + 21, - [33] = IPRIO_DEFAULT_LOWER + 22, - [32] = IPRIO_DEFAULT_LOWER + 23, + [39] = IPRIO_DEFAULT_LOWER, + [19] = IPRIO_DEFAULT_LOWER + 1, + [38] = IPRIO_DEFAULT_LOWER + 2, + [37] = IPRIO_DEFAULT_LOWER + 3, + [18] = IPRIO_DEFAULT_LOWER + 4, + [36] = IPRIO_DEFAULT_LOWER + 5, + + [35] = IPRIO_DEFAULT_LOWER + 6, + [17] = IPRIO_DEFAULT_LOWER + 7, + [34] = IPRIO_DEFAULT_LOWER + 8, + [33] = IPRIO_DEFAULT_LOWER + 9, + [16] = IPRIO_DEFAULT_LOWER + 10, + [32] = IPRIO_DEFAULT_LOWER + 11, }; uint8_t riscv_cpu_default_priority(int irq) @@ -1347,7 +1343,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (cause == RISCV_EXCP_SEMIHOST) { if (env->priv >= PRV_S) { - env->gpr[xA0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; return; } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 6dbe9b541fd..235f2a011e7 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "qemu/timer.h" #include "cpu.h" +#include "pmu.h" #include "qemu/main-loop.h" #include "exec/exec-all.h" #include "sysemu/cpu-timers.h" @@ -72,12 +73,72 @@ static RISCVException ctr(CPURISCVState *env, int csrno) #if !defined(CONFIG_USER_ONLY) CPUState *cs = env_cpu(env); RISCVCPU *cpu = RISCV_CPU(cs); + int ctr_index; + int base_csrno = CSR_HPMCOUNTER3; + bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; - if (!cpu->cfg.ext_counters) { - /* The Counters extensions is not enabled */ + if (rv32 && csrno >= CSR_CYCLEH) { + /* Offset for RV32 hpmcounternh counters */ + base_csrno += 0x80; + } + ctr_index = csrno - base_csrno; + + if (!cpu->cfg.pmu_num || ctr_index >= (cpu->cfg.pmu_num)) { + /* No counter is enabled in PMU or the counter is out of range */ return RISCV_EXCP_ILLEGAL_INST; } + if (env->priv == PRV_S) { + switch (csrno) { + case CSR_CYCLE: + if (!get_field(env->mcounteren, COUNTEREN_CY)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + case CSR_TIME: + if (!get_field(env->mcounteren, COUNTEREN_TM)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + case CSR_INSTRET: + if (!get_field(env->mcounteren, COUNTEREN_IR)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: + ctr_index = csrno - CSR_CYCLE; + if (!get_field(env->mcounteren, 1 << ctr_index)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + } + if (rv32) { + switch (csrno) { + case CSR_CYCLEH: + if (!get_field(env->mcounteren, COUNTEREN_CY)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + case CSR_TIMEH: + if (!get_field(env->mcounteren, COUNTEREN_TM)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + case CSR_INSTRETH: + if (!get_field(env->mcounteren, COUNTEREN_IR)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: + ctr_index = csrno - CSR_CYCLEH; + if (!get_field(env->mcounteren, 1 << ctr_index)) { + return RISCV_EXCP_ILLEGAL_INST; + } + break; + } + } + } + if (riscv_cpu_virt_enabled(env)) { switch (csrno) { case CSR_CYCLE: @@ -99,13 +160,14 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - if (!get_field(env->hcounteren, 1 << (csrno - CSR_HPMCOUNTER3)) && - get_field(env->mcounteren, 1 << (csrno - CSR_HPMCOUNTER3))) { + ctr_index = csrno - CSR_CYCLE; + if (!get_field(env->hcounteren, 1 << ctr_index) && + get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } break; } - if (riscv_cpu_mxl(env) == MXL_RV32) { + if (rv32) { switch (csrno) { case CSR_CYCLEH: if (!get_field(env->hcounteren, COUNTEREN_CY) && @@ -126,8 +188,9 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - if (!get_field(env->hcounteren, 1 << (csrno - CSR_HPMCOUNTER3H)) && - get_field(env->mcounteren, 1 << (csrno - CSR_HPMCOUNTER3H))) { + ctr_index = csrno - CSR_CYCLEH; + if (!get_field(env->hcounteren, 1 << ctr_index) && + get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } break; @@ -148,6 +211,35 @@ static RISCVException ctr32(CPURISCVState *env, int csrno) } #if !defined(CONFIG_USER_ONLY) +static RISCVException mctr(CPURISCVState *env, int csrno) +{ + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + 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; + } + ctr_index = csrno - base_csrno; + if (!cpu->cfg.pmu_num || ctr_index >= cpu->cfg.pmu_num) { + /* The PMU is not enabled or counter is out of range*/ + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException mctr32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return mctr(env, csrno); +} + static RISCVException any(CPURISCVState *env, int csrno) { return RISCV_EXCP_NONE; @@ -506,34 +598,28 @@ static int write_vcsr(CPURISCVState *env, int csrno, target_ulong val) } /* User Timers and Counters */ -static RISCVException read_instret(CPURISCVState *env, int csrno, - target_ulong *val) +static target_ulong get_ticks(bool shift) { + int64_t val; + target_ulong result; + #if !defined(CONFIG_USER_ONLY) if (icount_enabled()) { - *val = icount_get(); + val = icount_get(); } else { - *val = cpu_get_host_ticks(); + val = cpu_get_host_ticks(); } #else - *val = cpu_get_host_ticks(); + val = cpu_get_host_ticks(); #endif - return RISCV_EXCP_NONE; -} -static RISCVException read_instreth(CPURISCVState *env, int csrno, - target_ulong *val) -{ -#if !defined(CONFIG_USER_ONLY) - if (icount_enabled()) { - *val = icount_get() >> 32; + if (shift) { + result = val >> 32; } else { - *val = cpu_get_host_ticks() >> 32; + result = val; } -#else - *val = cpu_get_host_ticks() >> 32; -#endif - return RISCV_EXCP_NONE; + + return result; } #if defined(CONFIG_USER_ONLY) @@ -551,8 +637,139 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static int 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) +{ + *val = get_ticks(true); + return RISCV_EXCP_NONE; +} + #else /* CONFIG_USER_ONLY */ +static int 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 int write_mhpmevent(CPURISCVState *env, int csrno, target_ulong val) +{ + int evt_index = csrno - CSR_MCOUNTINHIBIT; + + env->mhpmevent_val[evt_index] = val; + + return RISCV_EXCP_NONE; +} + +static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) +{ + int ctr_idx = csrno - CSR_MCYCLE; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + + 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); + } else { + /* Other counters can keep incrementing from the given value */ + counter->mhpmcounter_prev = val; + } + + return RISCV_EXCP_NONE; +} + +static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) +{ + int ctr_idx = csrno - CSR_MCYCLEH; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + + counter->mhpmcounterh_val = val; + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + counter->mhpmcounterh_prev = get_ticks(true); + } else { + counter->mhpmcounterh_prev = val; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, + bool upper_half, uint32_t ctr_idx) +{ + PMUCTRState counter = env->pmu_ctrs[ctr_idx]; + target_ulong ctr_prev = upper_half ? counter.mhpmcounterh_prev : + counter.mhpmcounter_prev; + target_ulong ctr_val = upper_half ? counter.mhpmcounterh_val : + counter.mhpmcounter_val; + + if (get_field(env->mcountinhibit, BIT(ctr_idx))) { + /** + * Counter should not increment if inhibit bit is set. We can't really + * stop the icount counting. Just return the counter value written by + * the supervisor to indicate that counter was not incremented. + */ + if (!counter.started) { + *val = ctr_val; + return RISCV_EXCP_NONE; + } else { + /* Mark that the counter has been stopped */ + counter.started = false; + } + } + + /** + * The kernel computes the perf delta by subtracting the current value from + * the value it initialized previously (ctr_val). + */ + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + *val = get_ticks(upper_half) - ctr_prev + ctr_val; + } else { + *val = ctr_val; + } + + return RISCV_EXCP_NONE; +} + +static int read_hpmcounter(CPURISCVState *env, int csrno, target_ulong *val) +{ + uint16_t ctr_index; + + if (csrno >= CSR_MCYCLE && csrno <= CSR_MHPMCOUNTER31) { + ctr_index = csrno - CSR_MCYCLE; + } else if (csrno >= CSR_CYCLE && csrno <= CSR_HPMCOUNTER31) { + ctr_index = csrno - CSR_CYCLE; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + + return riscv_pmu_read_ctr(env, val, false, ctr_index); +} + +static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) +{ + uint16_t ctr_index; + + if (csrno >= CSR_MCYCLEH && csrno <= CSR_MHPMCOUNTER31H) { + ctr_index = csrno - CSR_MCYCLEH; + } else if (csrno >= CSR_CYCLEH && csrno <= CSR_HPMCOUNTER31H) { + ctr_index = csrno - CSR_CYCLEH; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + + return riscv_pmu_read_ctr(env, val, true, ctr_index); +} + static RISCVException read_time(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1040,14 +1257,6 @@ static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) return CSR_VSISELECT; case CSR_SIREG: return CSR_VSIREG; - case CSR_SSETEIPNUM: - return CSR_VSSETEIPNUM; - case CSR_SCLREIPNUM: - return CSR_VSCLREIPNUM; - case CSR_SSETEIENUM: - return CSR_VSSETEIENUM; - case CSR_SCLREIENUM: - return CSR_VSCLREIENUM; case CSR_STOPEI: return CSR_VSTOPEI; default: @@ -1202,124 +1411,6 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, return RISCV_EXCP_NONE; } -static int rmw_xsetclreinum(CPURISCVState *env, int csrno, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) -{ - int ret = -EINVAL; - bool set, pend, virt; - target_ulong priv, isel, vgein, xlen, nval, wmask; - - /* Translate CSR number for VS-mode */ - csrno = aia_xlate_vs_csrno(env, csrno); - - /* Decode register details from CSR number */ - virt = set = pend = false; - switch (csrno) { - case CSR_MSETEIPNUM: - priv = PRV_M; - set = true; - pend = true; - break; - case CSR_MCLREIPNUM: - priv = PRV_M; - pend = true; - break; - case CSR_MSETEIENUM: - priv = PRV_M; - set = true; - break; - case CSR_MCLREIENUM: - priv = PRV_M; - break; - case CSR_SSETEIPNUM: - priv = PRV_S; - set = true; - pend = true; - break; - case CSR_SCLREIPNUM: - priv = PRV_S; - pend = true; - break; - case CSR_SSETEIENUM: - priv = PRV_S; - set = true; - break; - case CSR_SCLREIENUM: - priv = PRV_S; - break; - case CSR_VSSETEIPNUM: - priv = PRV_S; - virt = true; - set = true; - pend = true; - break; - case CSR_VSCLREIPNUM: - priv = PRV_S; - virt = true; - pend = true; - break; - case CSR_VSSETEIENUM: - priv = PRV_S; - virt = true; - set = true; - break; - case CSR_VSCLREIENUM: - priv = PRV_S; - virt = true; - break; - default: - goto done; - }; - - /* IMSIC CSRs only available when machine implements IMSIC. */ - if (!env->aia_ireg_rmw_fn[priv]) { - goto done; - } - - /* Find the selected guest interrupt file */ - vgein = (virt) ? get_field(env->hstatus, HSTATUS_VGEIN) : 0; - - /* Selected guest interrupt file should be valid */ - if (virt && (!vgein || env->geilen < vgein)) { - goto done; - } - - /* Set/Clear CSRs always read zero */ - if (val) { - *val = 0; - } - - if (wr_mask) { - /* Get interrupt number */ - new_val &= wr_mask; - - /* Find target interrupt pending/enable register */ - xlen = riscv_cpu_mxl_bits(env); - isel = (new_val / xlen); - isel *= (xlen / IMSIC_EIPx_BITS); - isel += (pend) ? ISELECT_IMSIC_EIP0 : ISELECT_IMSIC_EIE0; - - /* Find the interrupt bit to be set/clear */ - wmask = ((target_ulong)1) << (new_val % xlen); - nval = (set) ? wmask : 0; - - /* Call machine specific IMSIC register emulation */ - ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], - AIA_MAKE_IREG(isel, priv, virt, - vgein, xlen), - NULL, nval, wmask); - } else { - ret = 0; - } - -done: - if (ret) { - return (riscv_cpu_virt_enabled(env) && virt) ? - RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; - } - return RISCV_EXCP_NONE; -} - static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { @@ -1393,6 +1484,40 @@ static RISCVException write_mtvec(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_mcountinhibit(CPURISCVState *env, int csrno, + target_ulong *val) +{ + if (env->priv_ver < PRIV_VERSION_1_11_0) { + return RISCV_EXCP_ILLEGAL_INST; + } + + *val = env->mcountinhibit; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, + target_ulong val) +{ + int cidx; + PMUCTRState *counter; + + if (env->priv_ver < PRIV_VERSION_1_11_0) { + return RISCV_EXCP_ILLEGAL_INST; + } + + env->mcountinhibit = val; + + /* Check if any other counter is also monitoring cycles/instructions */ + for (cidx = 0; cidx < RV_MAX_MHPMCOUNTERS; cidx++) { + if (!get_field(env->mcountinhibit, BIT(cidx))) { + counter = &env->pmu_ctrs[cidx]; + counter->started = true; + } + } + + return RISCV_EXCP_NONE; +} + static RISCVException read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) { @@ -3351,10 +3476,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VLENB] = { "vlenb", vs, read_vlenb, .min_priv_ver = PRIV_VERSION_1_12_0 }, /* User Timers and Counters */ - [CSR_CYCLE] = { "cycle", ctr, read_instret }, - [CSR_INSTRET] = { "instret", ctr, read_instret }, - [CSR_CYCLEH] = { "cycleh", ctr32, read_instreth }, - [CSR_INSTRETH] = { "instreth", ctr32, read_instreth }, + [CSR_CYCLE] = { "cycle", ctr, read_hpmcounter }, + [CSR_INSTRET] = { "instret", ctr, read_hpmcounter }, + [CSR_CYCLEH] = { "cycleh", ctr32, read_hpmcounterh }, + [CSR_INSTRETH] = { "instreth", ctr32, read_hpmcounterh }, /* * In privileged mode, the monitor will have to emulate TIME CSRs only if @@ -3368,10 +3493,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ - [CSR_MCYCLE] = { "mcycle", any, read_instret }, - [CSR_MINSTRET] = { "minstret", any, read_instret }, - [CSR_MCYCLEH] = { "mcycleh", any32, read_instreth }, - [CSR_MINSTRETH] = { "minstreth", any32, read_instreth }, + [CSR_MCYCLE] = { "mcycle", any, read_hpmcounter, write_mhpmcounter}, + [CSR_MINSTRET] = { "minstret", any, read_hpmcounter, write_mhpmcounter}, + [CSR_MCYCLEH] = { "mcycleh", any32, read_hpmcounterh, write_mhpmcounterh}, + [CSR_MINSTRETH] = { "minstreth", any32, read_hpmcounterh, write_mhpmcounterh}, /* Machine Information Registers */ [CSR_MVENDORID] = { "mvendorid", any, read_mvendorid }, @@ -3407,14 +3532,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIREG] = { "mireg", aia_any, NULL, NULL, rmw_xireg }, /* Machine-Level Interrupts (AIA) */ - [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, - - /* Machine-Level IMSIC Interface (AIA) */ - [CSR_MSETEIPNUM] = { "mseteipnum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MCLREIPNUM] = { "mclreipnum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MSETEIENUM] = { "mseteienum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MCLREIENUM] = { "mclreienum", aia_any, NULL, NULL, rmw_xsetclreinum }, [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, + [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, /* Virtual Interrupts for Supervisor Level (AIA) */ [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, @@ -3462,14 +3581,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SIREG] = { "sireg", aia_smode, NULL, NULL, rmw_xireg }, /* Supervisor-Level Interrupts (AIA) */ - [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, - - /* Supervisor-Level IMSIC Interface (AIA) */ - [CSR_SSETEIPNUM] = { "sseteipnum", aia_smode, NULL, NULL, rmw_xsetclreinum }, - [CSR_SCLREIPNUM] = { "sclreipnum", aia_smode, NULL, NULL, rmw_xsetclreinum }, - [CSR_SSETEIENUM] = { "sseteienum", aia_smode, NULL, NULL, rmw_xsetclreinum }, - [CSR_SCLREIENUM] = { "sclreienum", aia_smode, NULL, NULL, rmw_xsetclreinum }, [CSR_STOPEI] = { "stopei", aia_smode, NULL, NULL, rmw_xtopei }, + [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, /* Supervisor-Level High-Half CSRs (AIA) */ [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, @@ -3541,14 +3654,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, /* VS-Level Interrupts (H-extension with AIA) */ - [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, - - /* VS-Level IMSIC Interface (H-extension with AIA) */ - [CSR_VSSETEIPNUM] = { "vsseteipnum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSCLREIPNUM] = { "vsclreipnum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSSETEIENUM] = { "vsseteienum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSCLREIENUM] = { "vsclreienum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, + [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, @@ -3561,7 +3668,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Physical Memory Protection */ [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_11_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, @@ -3603,154 +3710,244 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, write_spmbase }, /* Performance Counters */ - [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_zero }, - [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_zero }, - [CSR_HPMCOUNTER5] = { "hpmcounter5", ctr, read_zero }, - [CSR_HPMCOUNTER6] = { "hpmcounter6", ctr, read_zero }, - [CSR_HPMCOUNTER7] = { "hpmcounter7", ctr, read_zero }, - [CSR_HPMCOUNTER8] = { "hpmcounter8", ctr, read_zero }, - [CSR_HPMCOUNTER9] = { "hpmcounter9", ctr, read_zero }, - [CSR_HPMCOUNTER10] = { "hpmcounter10", ctr, read_zero }, - [CSR_HPMCOUNTER11] = { "hpmcounter11", ctr, read_zero }, - [CSR_HPMCOUNTER12] = { "hpmcounter12", ctr, read_zero }, - [CSR_HPMCOUNTER13] = { "hpmcounter13", ctr, read_zero }, - [CSR_HPMCOUNTER14] = { "hpmcounter14", ctr, read_zero }, - [CSR_HPMCOUNTER15] = { "hpmcounter15", ctr, read_zero }, - [CSR_HPMCOUNTER16] = { "hpmcounter16", ctr, read_zero }, - [CSR_HPMCOUNTER17] = { "hpmcounter17", ctr, read_zero }, - [CSR_HPMCOUNTER18] = { "hpmcounter18", ctr, read_zero }, - [CSR_HPMCOUNTER19] = { "hpmcounter19", ctr, read_zero }, - [CSR_HPMCOUNTER20] = { "hpmcounter20", ctr, read_zero }, - [CSR_HPMCOUNTER21] = { "hpmcounter21", ctr, read_zero }, - [CSR_HPMCOUNTER22] = { "hpmcounter22", ctr, read_zero }, - [CSR_HPMCOUNTER23] = { "hpmcounter23", ctr, read_zero }, - [CSR_HPMCOUNTER24] = { "hpmcounter24", ctr, read_zero }, - [CSR_HPMCOUNTER25] = { "hpmcounter25", ctr, read_zero }, - [CSR_HPMCOUNTER26] = { "hpmcounter26", ctr, read_zero }, - [CSR_HPMCOUNTER27] = { "hpmcounter27", ctr, read_zero }, - [CSR_HPMCOUNTER28] = { "hpmcounter28", ctr, read_zero }, - [CSR_HPMCOUNTER29] = { "hpmcounter29", ctr, read_zero }, - [CSR_HPMCOUNTER30] = { "hpmcounter30", ctr, read_zero }, - [CSR_HPMCOUNTER31] = { "hpmcounter31", ctr, read_zero }, - - [CSR_MHPMCOUNTER3] = { "mhpmcounter3", any, read_zero }, - [CSR_MHPMCOUNTER4] = { "mhpmcounter4", any, read_zero }, - [CSR_MHPMCOUNTER5] = { "mhpmcounter5", any, read_zero }, - [CSR_MHPMCOUNTER6] = { "mhpmcounter6", any, read_zero }, - [CSR_MHPMCOUNTER7] = { "mhpmcounter7", any, read_zero }, - [CSR_MHPMCOUNTER8] = { "mhpmcounter8", any, read_zero }, - [CSR_MHPMCOUNTER9] = { "mhpmcounter9", any, read_zero }, - [CSR_MHPMCOUNTER10] = { "mhpmcounter10", any, read_zero }, - [CSR_MHPMCOUNTER11] = { "mhpmcounter11", any, read_zero }, - [CSR_MHPMCOUNTER12] = { "mhpmcounter12", any, read_zero }, - [CSR_MHPMCOUNTER13] = { "mhpmcounter13", any, read_zero }, - [CSR_MHPMCOUNTER14] = { "mhpmcounter14", any, read_zero }, - [CSR_MHPMCOUNTER15] = { "mhpmcounter15", any, read_zero }, - [CSR_MHPMCOUNTER16] = { "mhpmcounter16", any, read_zero }, - [CSR_MHPMCOUNTER17] = { "mhpmcounter17", any, read_zero }, - [CSR_MHPMCOUNTER18] = { "mhpmcounter18", any, read_zero }, - [CSR_MHPMCOUNTER19] = { "mhpmcounter19", any, read_zero }, - [CSR_MHPMCOUNTER20] = { "mhpmcounter20", any, read_zero }, - [CSR_MHPMCOUNTER21] = { "mhpmcounter21", any, read_zero }, - [CSR_MHPMCOUNTER22] = { "mhpmcounter22", any, read_zero }, - [CSR_MHPMCOUNTER23] = { "mhpmcounter23", any, read_zero }, - [CSR_MHPMCOUNTER24] = { "mhpmcounter24", any, read_zero }, - [CSR_MHPMCOUNTER25] = { "mhpmcounter25", any, read_zero }, - [CSR_MHPMCOUNTER26] = { "mhpmcounter26", any, read_zero }, - [CSR_MHPMCOUNTER27] = { "mhpmcounter27", any, read_zero }, - [CSR_MHPMCOUNTER28] = { "mhpmcounter28", any, read_zero }, - [CSR_MHPMCOUNTER29] = { "mhpmcounter29", any, read_zero }, - [CSR_MHPMCOUNTER30] = { "mhpmcounter30", any, read_zero }, - [CSR_MHPMCOUNTER31] = { "mhpmcounter31", any, read_zero }, - - [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_zero }, - [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_zero }, - [CSR_MHPMEVENT5] = { "mhpmevent5", any, read_zero }, - [CSR_MHPMEVENT6] = { "mhpmevent6", any, read_zero }, - [CSR_MHPMEVENT7] = { "mhpmevent7", any, read_zero }, - [CSR_MHPMEVENT8] = { "mhpmevent8", any, read_zero }, - [CSR_MHPMEVENT9] = { "mhpmevent9", any, read_zero }, - [CSR_MHPMEVENT10] = { "mhpmevent10", any, read_zero }, - [CSR_MHPMEVENT11] = { "mhpmevent11", any, read_zero }, - [CSR_MHPMEVENT12] = { "mhpmevent12", any, read_zero }, - [CSR_MHPMEVENT13] = { "mhpmevent13", any, read_zero }, - [CSR_MHPMEVENT14] = { "mhpmevent14", any, read_zero }, - [CSR_MHPMEVENT15] = { "mhpmevent15", any, read_zero }, - [CSR_MHPMEVENT16] = { "mhpmevent16", any, read_zero }, - [CSR_MHPMEVENT17] = { "mhpmevent17", any, read_zero }, - [CSR_MHPMEVENT18] = { "mhpmevent18", any, read_zero }, - [CSR_MHPMEVENT19] = { "mhpmevent19", any, read_zero }, - [CSR_MHPMEVENT20] = { "mhpmevent20", any, read_zero }, - [CSR_MHPMEVENT21] = { "mhpmevent21", any, read_zero }, - [CSR_MHPMEVENT22] = { "mhpmevent22", any, read_zero }, - [CSR_MHPMEVENT23] = { "mhpmevent23", any, read_zero }, - [CSR_MHPMEVENT24] = { "mhpmevent24", any, read_zero }, - [CSR_MHPMEVENT25] = { "mhpmevent25", any, read_zero }, - [CSR_MHPMEVENT26] = { "mhpmevent26", any, read_zero }, - [CSR_MHPMEVENT27] = { "mhpmevent27", any, read_zero }, - [CSR_MHPMEVENT28] = { "mhpmevent28", any, read_zero }, - [CSR_MHPMEVENT29] = { "mhpmevent29", any, read_zero }, - [CSR_MHPMEVENT30] = { "mhpmevent30", any, read_zero }, - [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_zero }, - - [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_zero }, - [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_zero }, - [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_zero }, - [CSR_HPMCOUNTER6H] = { "hpmcounter6h", ctr32, read_zero }, - [CSR_HPMCOUNTER7H] = { "hpmcounter7h", ctr32, read_zero }, - [CSR_HPMCOUNTER8H] = { "hpmcounter8h", ctr32, read_zero }, - [CSR_HPMCOUNTER9H] = { "hpmcounter9h", ctr32, read_zero }, - [CSR_HPMCOUNTER10H] = { "hpmcounter10h", ctr32, read_zero }, - [CSR_HPMCOUNTER11H] = { "hpmcounter11h", ctr32, read_zero }, - [CSR_HPMCOUNTER12H] = { "hpmcounter12h", ctr32, read_zero }, - [CSR_HPMCOUNTER13H] = { "hpmcounter13h", ctr32, read_zero }, - [CSR_HPMCOUNTER14H] = { "hpmcounter14h", ctr32, read_zero }, - [CSR_HPMCOUNTER15H] = { "hpmcounter15h", ctr32, read_zero }, - [CSR_HPMCOUNTER16H] = { "hpmcounter16h", ctr32, read_zero }, - [CSR_HPMCOUNTER17H] = { "hpmcounter17h", ctr32, read_zero }, - [CSR_HPMCOUNTER18H] = { "hpmcounter18h", ctr32, read_zero }, - [CSR_HPMCOUNTER19H] = { "hpmcounter19h", ctr32, read_zero }, - [CSR_HPMCOUNTER20H] = { "hpmcounter20h", ctr32, read_zero }, - [CSR_HPMCOUNTER21H] = { "hpmcounter21h", ctr32, read_zero }, - [CSR_HPMCOUNTER22H] = { "hpmcounter22h", ctr32, read_zero }, - [CSR_HPMCOUNTER23H] = { "hpmcounter23h", ctr32, read_zero }, - [CSR_HPMCOUNTER24H] = { "hpmcounter24h", ctr32, read_zero }, - [CSR_HPMCOUNTER25H] = { "hpmcounter25h", ctr32, read_zero }, - [CSR_HPMCOUNTER26H] = { "hpmcounter26h", ctr32, read_zero }, - [CSR_HPMCOUNTER27H] = { "hpmcounter27h", ctr32, read_zero }, - [CSR_HPMCOUNTER28H] = { "hpmcounter28h", ctr32, read_zero }, - [CSR_HPMCOUNTER29H] = { "hpmcounter29h", ctr32, read_zero }, - [CSR_HPMCOUNTER30H] = { "hpmcounter30h", ctr32, read_zero }, - [CSR_HPMCOUNTER31H] = { "hpmcounter31h", ctr32, read_zero }, - - [CSR_MHPMCOUNTER3H] = { "mhpmcounter3h", any32, read_zero }, - [CSR_MHPMCOUNTER4H] = { "mhpmcounter4h", any32, read_zero }, - [CSR_MHPMCOUNTER5H] = { "mhpmcounter5h", any32, read_zero }, - [CSR_MHPMCOUNTER6H] = { "mhpmcounter6h", any32, read_zero }, - [CSR_MHPMCOUNTER7H] = { "mhpmcounter7h", any32, read_zero }, - [CSR_MHPMCOUNTER8H] = { "mhpmcounter8h", any32, read_zero }, - [CSR_MHPMCOUNTER9H] = { "mhpmcounter9h", any32, read_zero }, - [CSR_MHPMCOUNTER10H] = { "mhpmcounter10h", any32, read_zero }, - [CSR_MHPMCOUNTER11H] = { "mhpmcounter11h", any32, read_zero }, - [CSR_MHPMCOUNTER12H] = { "mhpmcounter12h", any32, read_zero }, - [CSR_MHPMCOUNTER13H] = { "mhpmcounter13h", any32, read_zero }, - [CSR_MHPMCOUNTER14H] = { "mhpmcounter14h", any32, read_zero }, - [CSR_MHPMCOUNTER15H] = { "mhpmcounter15h", any32, read_zero }, - [CSR_MHPMCOUNTER16H] = { "mhpmcounter16h", any32, read_zero }, - [CSR_MHPMCOUNTER17H] = { "mhpmcounter17h", any32, read_zero }, - [CSR_MHPMCOUNTER18H] = { "mhpmcounter18h", any32, read_zero }, - [CSR_MHPMCOUNTER19H] = { "mhpmcounter19h", any32, read_zero }, - [CSR_MHPMCOUNTER20H] = { "mhpmcounter20h", any32, read_zero }, - [CSR_MHPMCOUNTER21H] = { "mhpmcounter21h", any32, read_zero }, - [CSR_MHPMCOUNTER22H] = { "mhpmcounter22h", any32, read_zero }, - [CSR_MHPMCOUNTER23H] = { "mhpmcounter23h", any32, read_zero }, - [CSR_MHPMCOUNTER24H] = { "mhpmcounter24h", any32, read_zero }, - [CSR_MHPMCOUNTER25H] = { "mhpmcounter25h", any32, read_zero }, - [CSR_MHPMCOUNTER26H] = { "mhpmcounter26h", any32, read_zero }, - [CSR_MHPMCOUNTER27H] = { "mhpmcounter27h", any32, read_zero }, - [CSR_MHPMCOUNTER28H] = { "mhpmcounter28h", any32, read_zero }, - [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", any32, read_zero }, - [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", any32, read_zero }, - [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32, read_zero }, + [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER5] = { "hpmcounter5", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER6] = { "hpmcounter6", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER7] = { "hpmcounter7", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER8] = { "hpmcounter8", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER9] = { "hpmcounter9", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER10] = { "hpmcounter10", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER11] = { "hpmcounter11", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER12] = { "hpmcounter12", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER13] = { "hpmcounter13", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER14] = { "hpmcounter14", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER15] = { "hpmcounter15", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER16] = { "hpmcounter16", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER17] = { "hpmcounter17", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER18] = { "hpmcounter18", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER19] = { "hpmcounter19", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER20] = { "hpmcounter20", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER21] = { "hpmcounter21", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER22] = { "hpmcounter22", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER23] = { "hpmcounter23", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER24] = { "hpmcounter24", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER25] = { "hpmcounter25", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER26] = { "hpmcounter26", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER27] = { "hpmcounter27", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER28] = { "hpmcounter28", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER29] = { "hpmcounter29", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER30] = { "hpmcounter30", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER31] = { "hpmcounter31", ctr, read_hpmcounter }, + + [CSR_MHPMCOUNTER3] = { "mhpmcounter3", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER4] = { "mhpmcounter4", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER5] = { "mhpmcounter5", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER6] = { "mhpmcounter6", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER7] = { "mhpmcounter7", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER8] = { "mhpmcounter8", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER9] = { "mhpmcounter9", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER10] = { "mhpmcounter10", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER11] = { "mhpmcounter11", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER12] = { "mhpmcounter12", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER13] = { "mhpmcounter13", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER14] = { "mhpmcounter14", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER15] = { "mhpmcounter15", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER16] = { "mhpmcounter16", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER17] = { "mhpmcounter17", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER18] = { "mhpmcounter18", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER19] = { "mhpmcounter19", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER20] = { "mhpmcounter20", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER21] = { "mhpmcounter21", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER22] = { "mhpmcounter22", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER23] = { "mhpmcounter23", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER24] = { "mhpmcounter24", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER25] = { "mhpmcounter25", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER26] = { "mhpmcounter26", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER27] = { "mhpmcounter27", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER28] = { "mhpmcounter28", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER29] = { "mhpmcounter29", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER30] = { "mhpmcounter30", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER31] = { "mhpmcounter31", mctr, read_hpmcounter, + write_mhpmcounter }, + + [CSR_MCOUNTINHIBIT] = { "mcountinhibit", any, read_mcountinhibit, + write_mcountinhibit, .min_priv_ver = PRIV_VERSION_1_11_0 }, + + [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT5] = { "mhpmevent5", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT6] = { "mhpmevent6", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT7] = { "mhpmevent7", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT8] = { "mhpmevent8", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT9] = { "mhpmevent9", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT10] = { "mhpmevent10", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT11] = { "mhpmevent11", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT12] = { "mhpmevent12", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT13] = { "mhpmevent13", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT14] = { "mhpmevent14", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT15] = { "mhpmevent15", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT16] = { "mhpmevent16", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT17] = { "mhpmevent17", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT18] = { "mhpmevent18", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT19] = { "mhpmevent19", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT20] = { "mhpmevent20", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT21] = { "mhpmevent21", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT22] = { "mhpmevent22", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT23] = { "mhpmevent23", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT24] = { "mhpmevent24", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT25] = { "mhpmevent25", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT26] = { "mhpmevent26", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT27] = { "mhpmevent27", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT28] = { "mhpmevent28", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT29] = { "mhpmevent29", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT30] = { "mhpmevent30", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, + write_mhpmevent }, + + [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER6H] = { "hpmcounter6h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER7H] = { "hpmcounter7h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER8H] = { "hpmcounter8h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER9H] = { "hpmcounter9h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER10H] = { "hpmcounter10h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER11H] = { "hpmcounter11h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER12H] = { "hpmcounter12h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER13H] = { "hpmcounter13h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER14H] = { "hpmcounter14h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER15H] = { "hpmcounter15h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER16H] = { "hpmcounter16h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER17H] = { "hpmcounter17h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER18H] = { "hpmcounter18h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER19H] = { "hpmcounter19h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER20H] = { "hpmcounter20h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER21H] = { "hpmcounter21h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER22H] = { "hpmcounter22h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER23H] = { "hpmcounter23h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER24H] = { "hpmcounter24h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER25H] = { "hpmcounter25h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER26H] = { "hpmcounter26h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER27H] = { "hpmcounter27h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER28H] = { "hpmcounter28h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER29H] = { "hpmcounter29h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER30H] = { "hpmcounter30h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER31H] = { "hpmcounter31h", ctr32, read_hpmcounterh }, + + [CSR_MHPMCOUNTER3H] = { "mhpmcounter3h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER4H] = { "mhpmcounter4h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER5H] = { "mhpmcounter5h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER6H] = { "mhpmcounter6h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER7H] = { "mhpmcounter7h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER8H] = { "mhpmcounter8h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER9H] = { "mhpmcounter9h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER10H] = { "mhpmcounter10h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER11H] = { "mhpmcounter11h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER12H] = { "mhpmcounter12h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER13H] = { "mhpmcounter13h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER14H] = { "mhpmcounter14h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER15H] = { "mhpmcounter15h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER16H] = { "mhpmcounter16h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER17H] = { "mhpmcounter17h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER18H] = { "mhpmcounter18h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER19H] = { "mhpmcounter19h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER20H] = { "mhpmcounter20h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER21H] = { "mhpmcounter21h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER22H] = { "mhpmcounter22h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER23H] = { "mhpmcounter23h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER24H] = { "mhpmcounter24h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER25H] = { "mhpmcounter25h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER26H] = { "mhpmcounter26h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER27H] = { "mhpmcounter27h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER28H] = { "mhpmcounter28h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 53613682e88..46f96ad74d4 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -75,6 +75,7 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) { #ifndef CONFIG_USER_ONLY if (has_ext(ctx, RVS)) { + decode_save_opc(ctx); gen_helper_sret(cpu_pc, cpu_env); tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; @@ -90,6 +91,7 @@ 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); gen_helper_mret(cpu_pc, cpu_env); tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; @@ -102,6 +104,7 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY + decode_save_opc(ctx); gen_set_pc_imm(ctx, ctx->pc_succ_insn); gen_helper_wfi(cpu_env); return true; @@ -113,6 +116,7 @@ static bool trans_wfi(DisasContext *ctx, arg_wfi *a) static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) { #ifndef CONFIG_USER_ONLY + decode_save_opc(ctx); gen_helper_tlb_flush(cpu_env); return true; #endif diff --git a/target/riscv/insn_trans/trans_rvh.c.inc b/target/riscv/insn_trans/trans_rvh.c.inc index cebcb3f8f60..4f8aecddc7d 100644 --- a/target/riscv/insn_trans/trans_rvh.c.inc +++ b/target/riscv/insn_trans/trans_rvh.c.inc @@ -169,6 +169,7 @@ 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); return true; #endif @@ -179,6 +180,7 @@ 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); return true; #endif diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index f1342f30f8e..ca8e3d1ea1f 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -32,17 +32,13 @@ static bool trans_c64_illegal(DisasContext *ctx, arg_empty *a) static bool trans_lui(DisasContext *ctx, arg_lui *a) { - if (a->rd != 0) { - gen_set_gpri(ctx, a->rd, a->imm); - } + gen_set_gpri(ctx, a->rd, a->imm); return true; } static bool trans_auipc(DisasContext *ctx, arg_auipc *a) { - if (a->rd != 0) { - gen_set_gpri(ctx, a->rd, a->imm + ctx->base.pc_next); - } + gen_set_gpri(ctx, a->rd, a->imm + ctx->base.pc_next); return true; } @@ -822,6 +818,8 @@ 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); /* We may have changed important cpu state -- exit to main loop. */ gen_set_pc_imm(ctx, ctx->pc_succ_insn); tcg_gen_exit_tb(NULL, 0); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 2a437b29a1c..dc182ca8111 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -279,7 +279,28 @@ static const VMStateDescription vmstate_envcfg = { VMSTATE_UINT64(env.menvcfg, RISCVCPU), VMSTATE_UINTTL(env.senvcfg, RISCVCPU), VMSTATE_UINT64(env.henvcfg, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool pmu_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + return cpu->cfg.pmu_num; +} + +static const VMStateDescription vmstate_pmu_ctr_state = { + .name = "cpu/pmu", + .version_id = 1, + .minimum_version_id = 1, + .needed = pmu_needed, + .fields = (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() } }; @@ -330,6 +351,10 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.siselect, RISCVCPU), VMSTATE_UINTTL(env.scounteren, RISCVCPU), VMSTATE_UINTTL(env.mcounteren, RISCVCPU), + VMSTATE_UINTTL(env.mcountinhibit, RISCVCPU), + VMSTATE_STRUCT_ARRAY(env.pmu_ctrs, RISCVCPU, RV_MAX_MHPMCOUNTERS, 0, + vmstate_pmu_ctr_state, PMUCTRState), + VMSTATE_UINTTL_ARRAY(env.mhpmevent_val, RISCVCPU, RV_MAX_MHPMEVENTS), VMSTATE_UINTTL(env.sscratch, RISCVCPU), VMSTATE_UINTTL(env.mscratch, RISCVCPU), VMSTATE_UINT64(env.mfromhost, RISCVCPU), diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 096249f3a30..2c1975e72c4 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -30,7 +30,8 @@ riscv_softmmu_ss.add(files( 'pmp.c', 'debug.c', 'monitor.c', - 'machine.c' + 'machine.c', + 'pmu.c' )) target_arch += {'riscv': riscv_ss} diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 151da3fa084..ea2b67d947b 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -167,6 +167,9 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) case PMP_AMATCH_TOR: sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ ea = (this_addr << 2) - 1u; + if (sa > ea) { + sa = ea = 0u; + } break; case PMP_AMATCH_NA4: diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c new file mode 100644 index 00000000000..000fe8da45e --- /dev/null +++ b/target/riscv/pmu.c @@ -0,0 +1,32 @@ +/* + * RISC-V PMU file. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "pmu.h" + +bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, + uint32_t target_ctr) +{ + return (target_ctr == 0) ? true : false; +} + +bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) +{ + return (target_ctr == 2) ? true : false; +} diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h new file mode 100644 index 00000000000..58a5bc3a408 --- /dev/null +++ b/target/riscv/pmu.h @@ -0,0 +1,28 @@ +/* + * RISC-V PMU header file. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "qemu/main-loop.h" +#include "exec/exec-all.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); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index b151c20674d..63b04e8a948 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -206,6 +206,13 @@ 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) +{ + assert(ctx->insn_start != NULL); + tcg_set_insn_start_param(ctx->insn_start, 1, ctx->opcode); + ctx->insn_start = NULL; +} + static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) { if (get_xl(ctx) == MXL_RV32) { @@ -230,22 +237,17 @@ static void generate_exception(DisasContext *ctx, int excp) ctx->base.is_jmp = DISAS_NORETURN; } -static void generate_exception_mtval(DisasContext *ctx, int excp) -{ - gen_set_pc_imm(ctx, ctx->base.pc_next); - tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); - ctx->base.is_jmp = DISAS_NORETURN; -} - static void gen_exception_illegal(DisasContext *ctx) { + tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), cpu_env, + offsetof(CPURISCVState, bins)); generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); } static void gen_exception_inst_addr_mis(DisasContext *ctx) { - generate_exception_mtval(ctx, RISCV_EXCP_INST_ADDR_MIS); + tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); + generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); } static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) @@ -640,6 +642,8 @@ static void gen_set_rm(DisasContext *ctx, int rm) return; } + /* 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)); } @@ -1018,13 +1022,6 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) /* Include decoders for factored-out extensions */ #include "decode-XVentanaCondOps.c.inc" -static inline void decode_save_opc(DisasContext *ctx, target_ulong opc) -{ - assert(ctx->insn_start != NULL); - tcg_set_insn_start_param(ctx->insn_start, 1, opc); - ctx->insn_start = NULL; -} - static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) { /* @@ -1041,7 +1038,6 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) /* Check for compressed insn */ if (extract16(opcode, 0, 2) != 3) { - decode_save_opc(ctx, opcode); if (!has_ext(ctx, RVC)) { gen_exception_illegal(ctx); } else { @@ -1056,7 +1052,6 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) opcode32 = deposit32(opcode32, 16, 16, translator_lduw(env, &ctx->base, ctx->base.pc_next + 2)); - decode_save_opc(ctx, opcode32); ctx->opcode = opcode32; ctx->pc_succ_insn = ctx->base.pc_next + 4; diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index aab9c47747e..10dadb002a8 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -158,6 +158,13 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) if (prefix == old_prefix) { return; } + /* + * Since prefix got aligned to 8k and memory increments are a multiple of + * 8k checking the first page is sufficient + */ + if (!mmu_absolute_addr_valid(prefix, true)) { + tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC()); + } env->psa = prefix; HELPER_LOG("prefix: %#x\n", prefix); diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index fd2433d6252..e2ee0056715 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -149,6 +149,7 @@ struct DisasContext { uint64_t pc_tmp; uint32_t ilen; enum cc_op cc_op; + bool exit_to_mainloop; }; /* Information carried about a condition to be evaluated. */ @@ -1123,19 +1124,9 @@ typedef struct { exiting the TB. */ #define DISAS_PC_UPDATED DISAS_TARGET_0 -/* We have emitted one or more goto_tb. No fixup required. */ -#define DISAS_GOTO_TB DISAS_TARGET_1 - /* We have updated the PC and CC values. */ #define DISAS_PC_CC_UPDATED DISAS_TARGET_2 -/* We are exiting the TB, but have neither emitted a goto_tb, nor - updated the PC for the next instruction to be executed. */ -#define DISAS_PC_STALE DISAS_TARGET_3 - -/* We are exiting the TB to the main loop. */ -#define DISAS_PC_STALE_NOCHAIN DISAS_TARGET_4 - /* Instruction flags */ #define IF_AFP1 0x0001 /* r1 is a fp reg for HFP/FPS instructions */ @@ -1189,7 +1180,7 @@ static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 0); - return DISAS_GOTO_TB; + return DISAS_NORETURN; } else { tcg_gen_movi_i64(psw_addr, dest); per_branch(s, false); @@ -1258,7 +1249,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 1); - ret = DISAS_GOTO_TB; + ret = DISAS_NORETURN; } else { /* Fallthru can use goto_tb, but taken branch cannot. */ /* Store taken branch destination before the brcond. This @@ -3029,7 +3020,8 @@ static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) @@ -3040,7 +3032,8 @@ static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_lra(DisasContext *s, DisasOps *o) @@ -3996,7 +3989,7 @@ static DisasJumpType op_sacf(DisasContext *s, DisasOps *o) { gen_helper_sacf(cpu_env, o->in2); /* Addressing mode has changed, so end the block. */ - return DISAS_PC_STALE; + return DISAS_TOO_MANY; } #endif @@ -4032,7 +4025,7 @@ static DisasJumpType op_sam(DisasContext *s, DisasOps *o) tcg_temp_free_i64(tsam); /* Always exit the TB, since we (may have) changed execution mode. */ - return DISAS_PC_STALE; + return DISAS_TOO_MANY; } static DisasJumpType op_sar(DisasContext *s, DisasOps *o) @@ -4290,7 +4283,8 @@ static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) { tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_stap(DisasContext *s, DisasOps *o) @@ -4555,7 +4549,8 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) } /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_stura(DisasContext *s, DisasOps *o) @@ -6565,13 +6560,13 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* io should be the last instruction in tb when icount is enabled */ if (unlikely(icount && ret == DISAS_NEXT)) { - ret = DISAS_PC_STALE; + ret = DISAS_TOO_MANY; } #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER) { /* An exception might be triggered, save PSW if not already done. */ - if (ret == DISAS_NEXT || ret == DISAS_PC_STALE) { + if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { tcg_gen_movi_i64(psw_addr, s->pc_tmp); } @@ -6598,6 +6593,7 @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->cc_op = CC_OP_DYNAMIC; dc->ex_value = dc->base.tb->cs_base; + dc->exit_to_mainloop = (dc->base.tb->flags & FLAG_MASK_PER) || dc->ex_value; } static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -6634,12 +6630,9 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) DisasContext *dc = container_of(dcbase, DisasContext, base); switch (dc->base.is_jmp) { - case DISAS_GOTO_TB: case DISAS_NORETURN: break; case DISAS_TOO_MANY: - case DISAS_PC_STALE: - case DISAS_PC_STALE_NOCHAIN: update_psw_addr(dc); /* FALLTHRU */ case DISAS_PC_UPDATED: @@ -6649,8 +6642,7 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* FALLTHRU */ case DISAS_PC_CC_UPDATED: /* Exit the TB, either by raising a debug exception or by return. */ - if ((dc->base.tb->flags & FLAG_MASK_PER) || - dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { + if (dc->exit_to_mainloop) { tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); diff --git a/target/s390x/tcg/vec_fpu_helper.c b/target/s390x/tcg/vec_fpu_helper.c index 2a618a10935..75cf605b9f4 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -824,7 +824,7 @@ static S390MinMaxRes vfmin_res(uint16_t dcmask_a, uint16_t dcmask_b, default: g_assert_not_reached(); } - } else if (unlikely(dcmask_a & dcmask_b & DCMASK_ZERO)) { + } else if (unlikely((dcmask_a & DCMASK_ZERO) && (dcmask_b & DCMASK_ZERO))) { switch (type) { case S390_MINMAX_TYPE_JAVA: return neg_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; @@ -874,7 +874,7 @@ static S390MinMaxRes vfmax_res(uint16_t dcmask_a, uint16_t dcmask_b, default: g_assert_not_reached(); } - } else if (unlikely(dcmask_a & dcmask_b & DCMASK_ZERO)) { + } else if (unlikely((dcmask_a & DCMASK_ZERO) && (dcmask_b & DCMASK_ZERO))) { const bool neg_a = dcmask_a & DCMASK_NEGATIVE; switch (type) { diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index de4483e43be..1cbd047ab34 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -371,6 +371,8 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define MULHWU XO31( 11) #define DIVW XO31(491) #define DIVWU XO31(459) +#define MODSW XO31(779) +#define MODUW XO31(267) #define CMP XO31( 0) #define CMPL XO31( 32) #define LHBRX XO31(790) @@ -403,6 +405,8 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define MULHDU XO31( 9) #define DIVD XO31(489) #define DIVDU XO31(457) +#define MODSD XO31(777) +#define MODUD XO31(265) #define LBZX XO31( 87) #define LHZX XO31(279) @@ -2806,6 +2810,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); break; + case INDEX_op_rem_i32: + tcg_out32(s, MODSW | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_remu_i32: + tcg_out32(s, MODUW | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_shl_i32: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -2947,6 +2959,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_divu_i64: tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); break; + case INDEX_op_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; case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args, false); @@ -3722,6 +3740,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_div_i32: case INDEX_op_divu_i32: + case INDEX_op_rem_i32: + case INDEX_op_remu_i32: case INDEX_op_nand_i32: case INDEX_op_nor_i32: case INDEX_op_muluh_i32: @@ -3732,6 +3752,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_nor_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: + case INDEX_op_rem_i64: + case INDEX_op_remu_i64: case INDEX_op_mulsh_i64: case INDEX_op_muluh_i64: return C_O1_I2(r, r, r); diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index e6cf72503f8..b5cd225cfa4 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -83,7 +83,7 @@ extern bool have_vsx; /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 0 +#define TCG_TARGET_HAS_rem_i32 have_isa_3_00 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_ext8s_i32 1 #define TCG_TARGET_HAS_ext16s_i32 1 @@ -117,7 +117,7 @@ extern bool have_vsx; #define TCG_TARGET_HAS_extrl_i64_i32 0 #define TCG_TARGET_HAS_extrh_i64_i32 0 #define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 0 +#define TCG_TARGET_HAS_rem_i64 have_isa_3_00 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_ext8s_i64 1 #define TCG_TARGET_HAS_ext16s_i64 1 diff --git a/tcg/region.c b/tcg/region.c index 71ea81d6713..88d6bb273f1 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -548,7 +548,7 @@ static int alloc_code_gen_buffer_anon(size_t size, int prot, #ifdef CONFIG_POSIX #include "qemu/memfd.h" -static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) +static int alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) { void *buf_rw = NULL, *buf_rx = MAP_FAILED; int fd = -1; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 98337c567a8..f3d7441e06e 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -823,13 +823,6 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) static void tcg_target_init(TCGContext *s) { -#if defined(CONFIG_DEBUG_TCG_INTERPRETER) - const char *envval = getenv("DEBUG_TCG"); - if (envval) { - qemu_set_log(strtol(envval, NULL, 0)); - } -#endif - /* The current code uses uint8_t for tcg operations. */ tcg_debug_assert(tcg_op_defs_max <= UINT8_MAX); diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 033e613f241..ceb36c4f7af 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -53,11 +53,6 @@ # error Unknown pointer size for tci target #endif -#ifdef CONFIG_DEBUG_TCG -/* Enable debug output. */ -#define CONFIG_DEBUG_TCG_INTERPRETER -#endif - /* Optional instructions. */ #define TCG_TARGET_HAS_bswap16_i32 1 diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index b656a70c55b..ed4853c805b 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -120,14 +120,15 @@ def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None): # qemu binary path does not match arch for powerpc, handle it if 'ppc64le' in arch: arch = 'ppc64' - qemu_bin_relative_path = os.path.join(".", bin_prefix + arch) - if is_readable_executable_file(qemu_bin_relative_path): - return qemu_bin_relative_path - - qemu_bin_from_bld_dir_path = os.path.join(BUILD_DIR, - qemu_bin_relative_path) - if is_readable_executable_file(qemu_bin_from_bld_dir_path): - return qemu_bin_from_bld_dir_path + qemu_bin_name = bin_prefix + arch + qemu_bin_paths = [ + os.path.join(".", qemu_bin_name), + os.path.join(BUILD_DIR, qemu_bin_name), + os.path.join(BUILD_DIR, "build", qemu_bin_name), + ] + for path in qemu_bin_paths: + if is_readable_executable_file(path): + return path return None diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 45a2ceda222..6b1533c17c8 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -1043,49 +1043,6 @@ def test_arm_vexpressa9(self): self.vm.add_args('-dtb', self.workdir + '/day16/vexpress-v2p-ca9.dtb') self.do_test_advcal_2018('16', tar_hash, 'winter.zImage') - def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:palmetto-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-palmetto.static.mtd') - image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def test_arm_ast2500_romulus_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:romulus-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-romulus.static.mtd') - image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def do_test_arm_aspeed(self, image): - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic') - self.vm.launch() - - self.wait_for_console_pattern("U-Boot 2016.07") - self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") - self.wait_for_console_pattern("Starting kernel ...") - self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") - self.wait_for_console_pattern( - "aspeed-smc 1e620000.spi: read control register: 203b0641") - self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") - self.wait_for_console_pattern("systemd[1]: Set hostname to") - def test_arm_ast2600_debian(self): """ :avocado: tags=arch:arm diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index 33090af1994..b4e35a3d074 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -5,8 +5,11 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +import time + from avocado_qemu import 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.utils import archive @@ -34,3 +37,204 @@ def test_ast1030_zephyros(self): wait_for_console_pattern(self, "Booting Zephyr OS") exec_command_and_wait_for_pattern(self, "help", "Available commands") + +class AST2x00Machine(QemuSystemTest): + + 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_buidroot_start(self, image, cpu_id): + 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') + self.wait_for_console_pattern('Aspeed EVB') + exec_command(self, 'root') + time.sleep(0.1) + + def do_test_arm_aspeed_buidroot_poweroff(self): + exec_command_and_wait_for_pattern(self, 'poweroff', + 'reboot: System halted'); + + def test_arm_ast2500_evb_builroot(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2500-evb + """ + + image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2500-evb/buildroot-2022.05/flash.img') + image_hash = ('549db6e9d8cdaf4367af21c36385a68bb465779c18b5e37094fc7343decccd3f') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + self.do_test_arm_aspeed_buidroot_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_buidroot_poweroff() + + def test_arm_ast2600_evb_builroot(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2600-evb + """ + + image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2022.05/flash.img') + image_hash = ('6cc9e7d128fd4fa1fd01c883af67593cae8072c3239a0b8b6ace857f3538a92d') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); + self.do_test_arm_aspeed_buidroot_start(image_path, '0xf00') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon0/temp1_input', '0') + self.vm.command('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon0/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); + + self.do_test_arm_aspeed_buidroot_poweroff() + + + def do_test_arm_aspeed_sdk_start(self, image, cpu_id): + 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) + + def test_arm_ast2500_evb_sdk(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2500-evb + """ + + image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' + 'download/v08.01/ast2500-default-obmc.tar.gz') + image_hash = ('5375f82b4c43a79427909342a1e18b4e48bd663e38466862145d27bb358796fd') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + archive.extract(image_path, self.workdir) + + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2500-default/image-bmc', '0x0') + self.wait_for_console_pattern('ast2500-default login:') + + def test_arm_ast2600_evb_sdk(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2600-evb + """ + + image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' + 'download/v08.01/ast2600-default-obmc.tar.gz') + image_hash = ('f12ef15e8c1f03a214df3b91c814515c5e2b2f56119021398c1dbdd626817d15') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + archive.extract(image_path, self.workdir) + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2600-default/image-bmc', '0xf00') + self.wait_for_console_pattern('ast2600-default login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', 'root@ast2600-default:~#') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') + self.vm.command('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index ef4518d9ebf..9a45e8890b2 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -140,6 +140,7 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ # Specialist build images, sometimes very limited tools docker-image-debian-tricore-cross: docker-image-debian10 docker-image-debian-all-test-cross: docker-image-debian10 +docker-image-debian-loongarch-cross: docker-image-debian11 docker-image-debian-microblaze-cross: docker-image-debian10 docker-image-debian-nios2-cross: docker-image-debian10 docker-image-debian-powerpc-test-cross: docker-image-debian11 @@ -149,6 +150,7 @@ docker-image-debian-riscv64-test-cross: docker-image-debian11 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-nios2-cross diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker new file mode 100644 index 00000000000..ca2469d2a86 --- /dev/null +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -0,0 +1,25 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian11 base image, +# using a prebuilt toolchains for LoongArch64 from: +# https://github.com/loongson/build-tools/releases +# +FROM qemu/debian11 + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + gettext \ + git \ + python3-minimal + +RUN curl -#SL https://github.com/loongson/build-tools/releases/download/2022.05.29/loongarch64-clfs-5.0-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 diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 8bd0979f67e..2b4f00b9165 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -1,3 +1,6 @@ +if 'CONFIG_TCG' not in config_all + 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' diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 9e923d6a59f..54e935acf28 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -60,6 +60,11 @@ if sudo -n losetup &>/dev/null; then else loopdev=false + # Check for usable FUSE in the host environment: + if test ! -c "/dev/fuse"; then + _notrun 'No passwordless sudo nor usable /dev/fuse' + fi + # QSD --export fuse will either yield "Parameter 'id' is missing" # or "Invalid parameter 'fuse'", depending on whether there is # FUSE support or not. diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out index 06479415312..26fb347c5da 100644 --- a/tests/qemu-iotests/223.out +++ b/tests/qemu-iotests/223.out @@ -93,7 +93,7 @@ exports available: 3 export: 'n2' description: some text size: 4194304 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0xded ( flush fua trim zeroes df multi cache fast-zero ) min block: 1 opt block: 4096 max block: 33554432 @@ -212,7 +212,7 @@ exports available: 3 export: 'n2' description: some text size: 4194304 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0xded ( flush fua trim zeroes df multi cache fast-zero ) min block: 1 opt block: 4096 max block: 33554432 diff --git a/tests/qemu-iotests/307.out b/tests/qemu-iotests/307.out index ec8d2be0e0a..390f05d1b78 100644 --- a/tests/qemu-iotests/307.out +++ b/tests/qemu-iotests/307.out @@ -83,7 +83,7 @@ exports available: 2 export: 'export1' description: This is the writable second export size: 67108864 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0xded ( flush fua trim zeroes df multi cache fast-zero ) min block: XXX opt block: XXX max block: XXX @@ -109,7 +109,7 @@ exports available: 1 export: 'export1' description: This is the writable second export size: 67108864 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0xded ( flush fua trim zeroes df multi cache fast-zero ) min block: XXX opt block: XXX max block: XXX diff --git a/tests/qemu-iotests/mypy.ini b/tests/qemu-iotests/mypy.ini index 4c0339f5589..d66ffc2e3c7 100644 --- a/tests/qemu-iotests/mypy.ini +++ b/tests/qemu-iotests/mypy.ini @@ -9,4 +9,4 @@ no_implicit_optional = True scripts_are_modules = True warn_redundant_casts = True warn_unused_configs = True -warn_unused_ignores = True +warn_unused_ignores = False diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index 32ab77b8bb9..f4f823a9915 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -51,3 +51,8 @@ notes=FIXME, # Maximum number of characters on a single line. max-line-length=79 + + +[SIMILARITIES] + +min-similarity-lines=6 diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write new file mode 100755 index 00000000000..2ffe092b318 --- /dev/null +++ b/tests/qemu-iotests/tests/copy-before-write @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +# group: auto backup +# +# Copyright (c) 2022 Virtuozzo International GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import re + +from qemu.machine import QEMUMachine + +import iotests +from iotests import qemu_img_create, qemu_io + + +temp_img = os.path.join(iotests.test_dir, 'temp') +source_img = os.path.join(iotests.test_dir, 'source') +size = '1M' + + +class TestCbwError(iotests.QMPTestCase): + def tearDown(self): + self.vm.shutdown() + os.remove(temp_img) + os.remove(source_img) + + def setUp(self): + qemu_img_create('-f', iotests.imgfmt, source_img, size) + qemu_img_create('-f', iotests.imgfmt, temp_img, size) + qemu_io('-c', 'write 0 1M', source_img) + + opts = ['-nodefaults', '-display', 'none', '-machine', 'none'] + self.vm = QEMUMachine(iotests.qemu_prog, opts, + base_temp_dir=iotests.test_dir, + sock_dir=iotests.sock_dir) + self.vm.launch() + + def do_cbw_error(self, on_cbw_error): + result = self.vm.qmp('blockdev-add', { + 'node-name': 'cbw', + 'driver': 'copy-before-write', + 'on-cbw-error': on_cbw_error, + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img, + } + }, + 'target': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': temp_img + }, + 'inject-error': [ + { + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + } + ] + } + } + }) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('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"') + self.assert_qmp(result, 'return', '') + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io access "read 0 1M"') + self.assert_qmp(result, 'return', '') + + self.vm.shutdown() + log = self.vm.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qemu_io(log) + return log + + def test_break_snapshot_on_cbw_error(self): + """break-snapshot behavior: + Guest write succeed, but further snapshot-read fails, as snapshot is + broken. + """ + log = self.do_cbw_error('break-snapshot') + + self.assertEqual(log, """\ +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Permission denied +""") + + def test_break_guest_write_on_cbw_error(self): + """break-guest-write behavior: + Guest write fails, but snapshot-access continues working and further + snapshot-read succeeds. + """ + log = self.do_cbw_error('break-guest-write') + + self.assertEqual(log, """\ +write failed: Input/output error +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def do_cbw_timeout(self, on_cbw_error): + result = self.vm.qmp('object-add', { + 'qom-type': 'throttle-group', + 'id': 'group0', + 'limits': {'bps-write': 300 * 1024} + }) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-add', { + 'node-name': 'cbw', + 'driver': 'copy-before-write', + 'on-cbw-error': on_cbw_error, + 'cbw-timeout': 1, + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img, + } + }, + 'target': { + 'driver': 'throttle', + 'throttle-group': 'group0', + 'file': { + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': temp_img + } + } + } + }) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('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"') + self.assert_qmp(result, 'return', '') + + # We need second write to trigger throttling + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io cbw "write 512K 512K"') + self.assert_qmp(result, 'return', '') + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io access "read 0 1M"') + self.assert_qmp(result, 'return', '') + + self.vm.shutdown() + log = self.vm.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qemu_io(log) + return log + + def test_timeout_break_guest(self): + log = self.do_cbw_timeout('break-guest-write') + # macOS and FreeBSD tend to represent ETIMEDOUT as + # "Operation timed out", when Linux prefer + # "Connection timed out" + log = log.replace('Operation timed out', + 'Connection timed out') + self.assertEqual(log, """\ +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: Connection timed out +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def test_timeout_break_snapshot(self): + log = self.do_cbw_timeout('break-snapshot') + self.assertEqual(log, """\ +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Permission denied +""") + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], + supported_protocols=['file'], + required_fmts=['copy-before-write']) diff --git a/tests/qemu-iotests/tests/copy-before-write.out b/tests/qemu-iotests/tests/copy-before-write.out new file mode 100644 index 00000000000..89968f35d78 --- /dev/null +++ b/tests/qemu-iotests/tests/copy-before-write.out @@ -0,0 +1,5 @@ +.... +---------------------------------------------------------------------- +Ran 4 tests + +OK diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index bac63e8742f..d38f51d7190 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -28,6 +28,11 @@ #include "qapi/qmp/qdict.h" #include "libqtest-single.h" +#define AST2600_GPIO_BASE 0x1E780000 + +#define GPIO_ABCD_DATA_VALUE 0x000 +#define GPIO_ABCD_DIRECTION 0x004 + static void test_set_colocated_pins(const void *data) { QTestState *s = (QTestState *)data; @@ -46,6 +51,27 @@ static void test_set_colocated_pins(const void *data) g_assert(!qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV7")); } +static void test_set_input_pins(const void *data) +{ + QTestState *s = (QTestState *)data; + char name[16]; + uint32_t value; + + qtest_writel(s, AST2600_GPIO_BASE + GPIO_ABCD_DIRECTION, 0x00000000); + for (char c = 'A'; c <= 'D'; c++) { + for (int i = 0; i < 8; i++) { + sprintf(name, "gpio%c%d", c, i); + qtest_qom_set_bool(s, "/machine/soc/gpio", name, true); + } + } + value = qtest_readl(s, AST2600_GPIO_BASE + GPIO_ABCD_DATA_VALUE); + g_assert_cmphex(value, ==, 0xffffffff); + + qtest_writel(s, AST2600_GPIO_BASE + GPIO_ABCD_DATA_VALUE, 0x00000000); + value = qtest_readl(s, AST2600_GPIO_BASE + GPIO_ABCD_DATA_VALUE); + g_assert_cmphex(value, ==, 0xffffffff); +} + int main(int argc, char **argv) { QTestState *s; @@ -56,6 +82,7 @@ int main(int argc, char **argv) s = qtest_init("-machine ast2600-evb"); qtest_add_data_func("/ast2600/gpio/set_colocated_pins", s, test_set_colocated_pins); + qtest_add_data_func("/ast2600/gpio/set_input_pins", s, test_set_input_pins); r = g_test_run(); qtest_quit(s); diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index ec233315e6c..05ce941566e 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -56,7 +56,9 @@ enum { BULK_ERASE = 0xc7, READ = 0x03, PP = 0x02, + WRSR = 0x1, WREN = 0x6, + SRWD = 0x80, RESET_ENABLE = 0x66, RESET_MEMORY = 0x99, EN_4BYTE_ADDR = 0xB7, @@ -135,6 +137,9 @@ static void flash_reset(void) spi_ctrl_start_user(); writeb(ASPEED_FLASH_BASE, RESET_ENABLE); writeb(ASPEED_FLASH_BASE, RESET_MEMORY); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + writeb(ASPEED_FLASH_BASE, WRDI); spi_ctrl_stop_user(); spi_conf_remove(CONF_ENABLE_W0); @@ -187,6 +192,24 @@ static void read_page_mem(uint32_t addr, uint32_t *page) } } +static void write_page_mem(uint32_t addr, uint32_t write_value) +{ + spi_ctrl_setmode(CTRL_WRITEMODE, PP); + + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE + addr + i * 4, write_value); + } +} + +static void assert_page_mem(uint32_t addr, uint32_t expected_value) +{ + uint32_t page[FLASH_PAGE_SIZE / 4]; + read_page_mem(addr, page); + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, expected_value); + } +} + static void test_erase_sector(void) { uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; @@ -195,21 +218,41 @@ static void test_erase_sector(void) spi_conf(CONF_ENABLE_W0); + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); + 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(); - /* Previous page should be full of zeroes as backend is not - * initialized */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); + /* Check the page is correctly written */ + read_page(some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0x0); + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); } - /* But this one was erased */ + 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); @@ -226,11 +269,31 @@ static void test_erase_all(void) spi_conf(CONF_ENABLE_W0); - /* Check some random page. Should be full of zeroes as backend is - * not initialized */ + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, PP); + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(); + + /* Check the page is correctly written */ read_page(some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0x0); + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); } spi_ctrl_start_user(); @@ -238,7 +301,7 @@ static void test_erase_all(void) writeb(ASPEED_FLASH_BASE, BULK_ERASE); spi_ctrl_stop_user(); - /* Recheck that some random page */ + /* Check the page is erased */ read_page(some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); @@ -299,6 +362,14 @@ static void test_read_page_mem(void) spi_conf(CONF_ENABLE_W0); spi_ctrl_start_user(); writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, PP); + writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); + } spi_ctrl_stop_user(); spi_conf_remove(CONF_ENABLE_W0); @@ -390,6 +461,153 @@ static void test_read_status_reg(void) flash_reset(); } +static void test_status_reg_write_protection(void) +{ + uint8_t r; + + spi_conf(CONF_ENABLE_W0); + + /* default case: WP# is high and SRWD is low -> status register writable */ + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, SRWD); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# high and SRWD high -> status register writable */ + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, 0); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + g_assert_cmphex(r & SRWD, ==, 0); + + /* WP# low and SRWD low -> status register writable */ + qtest_set_irq_in(global_qtest, + "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, SRWD); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# low and SRWD high -> status register NOT writable */ + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, 0); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + /* write is not successful */ + g_assert_cmphex(r & SRWD, ==, SRWD); + + qtest_set_irq_in(global_qtest, + "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); + flash_reset(); +} + +static void test_write_block_protect(void) +{ + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(1 << CRTL_EXTENDED0); + spi_conf(CONF_ENABLE_W0); + + uint32_t bp_bits = 0b0; + + for (int i = 0; i < 16; i++) { + bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, bp_bits); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_stop_user(); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = n_sectors - num_protected_sectors; + uint32_t protection_end = n_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(addr, 0xffffffff); + write_page_mem(addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(addr, expected_value); + } + } + + flash_reset(); +} + +static void test_write_block_protect_bottom_bit(void) +{ + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(1 << CRTL_EXTENDED0); + spi_conf(CONF_ENABLE_W0); + + /* top bottom bit is enabled */ + uint32_t bp_bits = 0b00100 << 3; + + for (int i = 0; i < 16; i++) { + bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, bp_bits); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_stop_user(); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = 0; + uint32_t protection_end = num_protected_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(addr, 0xffffffff); + write_page_mem(addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(addr, expected_value); + } + } + + flash_reset(); +} + static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX"; int main(int argc, char **argv) @@ -416,7 +634,14 @@ int main(int argc, char **argv) qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); + qtest_add_func("/ast2400/smc/status_reg_write_protection", + test_status_reg_write_protection); + qtest_add_func("/ast2400/smc/write_block_protect", + test_write_block_protect); + qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit", + test_write_block_protect_bottom_bit); + flash_reset(); ret = g_test_run(); qtest_quit(global_qtest); diff --git a/tests/qtest/bcm2835-dma-test.c b/tests/qtest/bcm2835-dma-test.c new file mode 100644 index 00000000000..8293d822b94 --- /dev/null +++ b/tests/qtest/bcm2835-dma-test.c @@ -0,0 +1,118 @@ +/* + * QTest testcase for BCM283x DMA engine (on Raspberry Pi 3) + * and its interrupts coming to Interrupt Controller. + * + * Copyright (c) 2022 Auriga LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Offsets in raspi3b platform: */ +#define RASPI3_DMA_BASE 0x3f007000 +#define RASPI3_IC_BASE 0x3f00b200 + +/* Used register/fields definitions */ + +/* DMA engine registers: */ +#define BCM2708_DMA_CS 0 +#define BCM2708_DMA_ACTIVE (1 << 0) +#define BCM2708_DMA_INT (1 << 2) + +#define BCM2708_DMA_ADDR 0x04 + +#define BCM2708_DMA_INT_STATUS 0xfe0 + +/* DMA Trasfer Info fields: */ +#define BCM2708_DMA_INT_EN (1 << 0) +#define BCM2708_DMA_D_INC (1 << 4) +#define BCM2708_DMA_S_INC (1 << 8) + +/* Interrupt controller registers: */ +#define IRQ_PENDING_BASIC 0x00 +#define IRQ_GPU_PENDING1_AGGR (1 << 8) +#define IRQ_PENDING_1 0x04 +#define IRQ_ENABLE_1 0x10 + +/* Data for the test: */ +#define SCB_ADDR 256 +#define S_ADDR 32 +#define D_ADDR 64 +#define TXFR_LEN 32 +const uint32_t check_data = 0x12345678; + +static void bcm2835_dma_test_interrupt(int dma_c, int irq_line) +{ + uint64_t dma_base = RASPI3_DMA_BASE + dma_c * 0x100; + int gpu_irq_line = 16 + irq_line; + + /* Check that interrupts are silent by default: */ + writel(RASPI3_IC_BASE + IRQ_ENABLE_1, 1 << gpu_irq_line); + int isr = readl(dma_base + BCM2708_DMA_INT_STATUS); + g_assert_cmpint(isr, ==, 0); + uint32_t reg0 = readl(dma_base + BCM2708_DMA_CS); + g_assert_cmpint(reg0, ==, 0); + uint32_t ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); + g_assert_cmpint(ic_pending, ==, 0); + uint32_t gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); + g_assert_cmpint(gpu_pending1, ==, 0); + + /* Prepare Control Block: */ + writel(SCB_ADDR + 0, BCM2708_DMA_S_INC | BCM2708_DMA_D_INC | + BCM2708_DMA_INT_EN); /* transfer info */ + writel(SCB_ADDR + 4, S_ADDR); /* source address */ + writel(SCB_ADDR + 8, D_ADDR); /* destination address */ + writel(SCB_ADDR + 12, TXFR_LEN); /* transfer length */ + writel(dma_base + BCM2708_DMA_ADDR, SCB_ADDR); + + writel(S_ADDR, check_data); + for (int word = S_ADDR + 4; word < S_ADDR + TXFR_LEN; word += 4) { + writel(word, ~check_data); + } + /* Perform the transfer: */ + writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_ACTIVE); + + /* Check that destination == source: */ + uint32_t data = readl(D_ADDR); + g_assert_cmpint(data, ==, check_data); + for (int word = D_ADDR + 4; word < D_ADDR + TXFR_LEN; word += 4) { + data = readl(word); + g_assert_cmpint(data, ==, ~check_data); + } + + /* Check that interrupt status is set both in DMA and IC controllers: */ + isr = readl(RASPI3_DMA_BASE + BCM2708_DMA_INT_STATUS); + g_assert_cmpint(isr, ==, 1 << dma_c); + + ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); + g_assert_cmpint(ic_pending, ==, IRQ_GPU_PENDING1_AGGR); + + gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); + g_assert_cmpint(gpu_pending1, ==, 1 << gpu_irq_line); + + /* Clean up, clear interrupt: */ + writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_INT); +} + +static void bcm2835_dma_test_interrupts(void) +{ + /* DMA engines 0--10 have separate IRQ lines, 11--14 - only one: */ + bcm2835_dma_test_interrupt(0, 0); + bcm2835_dma_test_interrupt(10, 10); + bcm2835_dma_test_interrupt(11, 11); + bcm2835_dma_test_interrupt(14, 11); +} + +int main(int argc, char **argv) +{ + int ret; + g_test_init(&argc, &argv, NULL); + qtest_add_func("/bcm2835/dma/test_interrupts", + bcm2835_dma_test_interrupts); + qtest_start("-machine raspi3b"); + ret = g_test_run(); + qtest_end(); + return ret; +} diff --git a/tests/qtest/fuzz-lsi53c895a-test.c b/tests/qtest/fuzz-lsi53c895a-test.c index 2e8e67859ef..b23d3ecf45e 100644 --- a/tests/qtest/fuzz-lsi53c895a-test.c +++ b/tests/qtest/fuzz-lsi53c895a-test.c @@ -8,6 +8,79 @@ #include "qemu/osdep.h" #include "libqtest.h" +/* + * This used to trigger a UAF in lsi_do_msgout() + * https://gitlab.com/qemu-project/qemu/-/issues/972 + */ +static void test_lsi_do_msgout_cancel_req(void) +{ + QTestState *s; + + if (sizeof(void *) == 4) { + g_test_skip("memory size too big for 32-bit build"); + return; + } + + s = qtest_init("-M q35 -m 4G -display none -nodefaults " + "-device lsi53c895a,id=scsi " + "-device scsi-hd,drive=disk0 " + "-drive file=null-co://,id=disk0,if=none,format=raw"); + + qtest_outl(s, 0xcf8, 0x80000810); + qtest_outl(s, 0xcf8, 0xc000); + qtest_outl(s, 0xcf8, 0x80000810); + qtest_outw(s, 0xcfc, 0x7); + qtest_outl(s, 0xcf8, 0x80000810); + qtest_outl(s, 0xcfc, 0xc000); + qtest_outl(s, 0xcf8, 0x80000804); + qtest_outw(s, 0xcfc, 0x05); + qtest_writeb(s, 0x69736c10, 0x08); + qtest_writeb(s, 0x69736c13, 0x58); + qtest_writeb(s, 0x69736c1a, 0x01); + qtest_writeb(s, 0x69736c1b, 0x06); + qtest_writeb(s, 0x69736c22, 0x01); + qtest_writeb(s, 0x69736c23, 0x07); + qtest_writeb(s, 0x69736c2b, 0x02); + qtest_writeb(s, 0x69736c48, 0x08); + qtest_writeb(s, 0x69736c4b, 0x58); + qtest_writeb(s, 0x69736c52, 0x04); + qtest_writeb(s, 0x69736c53, 0x06); + qtest_writeb(s, 0x69736c5b, 0x02); + qtest_outl(s, 0xc02d, 0x697300); + qtest_writeb(s, 0x5a554662, 0x01); + qtest_writeb(s, 0x5a554663, 0x07); + qtest_writeb(s, 0x5a55466a, 0x10); + qtest_writeb(s, 0x5a55466b, 0x22); + qtest_writeb(s, 0x5a55466c, 0x5a); + qtest_writeb(s, 0x5a55466d, 0x5a); + qtest_writeb(s, 0x5a55466e, 0x34); + qtest_writeb(s, 0x5a55466f, 0x5a); + qtest_writeb(s, 0x5a345a5a, 0x77); + qtest_writeb(s, 0x5a345a5b, 0x55); + qtest_writeb(s, 0x5a345a5c, 0x51); + qtest_writeb(s, 0x5a345a5d, 0x27); + qtest_writeb(s, 0x27515577, 0x41); + qtest_outl(s, 0xc02d, 0x5a5500); + qtest_writeb(s, 0x364001d0, 0x08); + qtest_writeb(s, 0x364001d3, 0x58); + qtest_writeb(s, 0x364001da, 0x01); + qtest_writeb(s, 0x364001db, 0x26); + qtest_writeb(s, 0x364001dc, 0x0d); + qtest_writeb(s, 0x364001dd, 0xae); + qtest_writeb(s, 0x364001de, 0x41); + qtest_writeb(s, 0x364001df, 0x5a); + qtest_writeb(s, 0x5a41ae0d, 0xf8); + qtest_writeb(s, 0x5a41ae0e, 0x36); + qtest_writeb(s, 0x5a41ae0f, 0xd7); + qtest_writeb(s, 0x5a41ae10, 0x36); + qtest_writeb(s, 0x36d736f8, 0x0c); + qtest_writeb(s, 0x36d736f9, 0x80); + qtest_writeb(s, 0x36d736fa, 0x0d); + qtest_outl(s, 0xc02d, 0x364000); + + qtest_quit(s); +} + /* * This used to trigger the assert in lsi_do_dma() * https://bugs.launchpad.net/qemu/+bug/697510 @@ -44,5 +117,8 @@ int main(int argc, char **argv) qtest_add_func("fuzz/lsi53c895a/lsi_do_dma_empty_queue", test_lsi_do_dma_empty_queue); + qtest_add_func("fuzz/lsi53c895a/lsi_do_msgout_cancel_req", + test_lsi_do_msgout_cancel_req); + return g_test_run(); } diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 0ad4ba9e94d..2b3bc1fb9df 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -158,8 +158,6 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) { char *target_name; - const char *bindir; - char *datadir; GString *cmd_line; gchar *pretty_cmd_line; bool serialize = false; @@ -174,22 +172,6 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) target_name = strstr(**argv, "-target-"); if (target_name) { /* The binary name specifies the target */ target_name += strlen("-target-"); - /* - * With oss-fuzz, the executable is kept in the root of a directory (we - * cannot assume the path). All data (including bios binaries) must be - * in the same dir, or a subdir. Thus, we cannot place the pc-bios so - * that it would be in exec_dir/../pc-bios. - * As a workaround, oss-fuzz allows us to use argv[0] to get the - * location of the executable. Using this we add exec_dir/pc-bios to - * the datadirs. - */ - bindir = qemu_get_exec_dir(); - datadir = g_build_filename(bindir, "pc-bios", NULL); - if (g_file_test(datadir, G_FILE_TEST_IS_DIR)) { - qemu_add_data_dir(datadir); - } else { - g_free(datadir); - } } else if (*argc > 1) { /* The target is specified as an argument */ target_name = (*argv)[1]; if (!strstr(target_name, "--fuzz-target=")) { diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index d0f8cd99024..f92fab479fd 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -54,6 +54,7 @@ static struct arch2cpu cpus_map[] = { { "riscv64", "rv64" }, { "riscv32", "rv32" }, { "rx", "rx62n" }, + { "loongarch64", "la464"}, }; static const char *get_cpu_model_by_arch(const char *arch) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 31287a91739..3a474010e49 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -218,7 +218,8 @@ qtests_aarch64 = \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', - 'migration-test'] + 'migration-test', + 'bcm2835-dma-test'] qtests_s390x = \ (slirp.found() ? ['pxe-test', 'test-netfilter'] : []) + \ diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index a6aa59e4e68..c6fbeb39748 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -15,6 +15,14 @@ #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 got_stop; static void check_stop_event(QTestState *who) @@ -75,6 +83,28 @@ QDict *wait_command(QTestState *who, const char *command, ...) return ret; } +/* + * Execute the qmp command only + */ +QDict *qmp_command(QTestState *who, const char *command, ...) +{ + va_list ap; + QDict *resp, *ret; + + va_start(ap, command); + resp = qtest_vqmp(who, command, ap); + va_end(ap); + + g_assert(!qdict_haskey(resp, "error")); + g_assert(qdict_haskey(resp, "return")); + + ret = qdict_get_qdict(resp, "return"); + qobject_ref(ret); + qobject_unref(resp); + + return ret; +} + /* * Send QMP command "migrate". * Arguments are built from @fmt... (formatted like @@ -166,8 +196,11 @@ static bool check_migration_status(QTestState *who, const char *goal, void wait_for_migration_status(QTestState *who, const char *goal, const char **ungoals) { + g_test_timer_start(); while (!check_migration_status(who, goal, ungoals)) { usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); } } @@ -178,6 +211,7 @@ void wait_for_migration_complete(QTestState *who) void wait_for_migration_fail(QTestState *from, bool allow_active) { + g_test_timer_start(); QDict *rsp_return; char *status; bool failed; @@ -193,6 +227,8 @@ void wait_for_migration_fail(QTestState *from, bool allow_active) g_assert(result); failed = !strcmp(status, "failed"); g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); } while (!failed); /* Is the machine currently running? */ diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 78587c2b829..59561898d04 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -23,6 +23,8 @@ QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); G_GNUC_PRINTF(2, 3) QDict *wait_command(QTestState *who, const char *command, ...); +QDict *qmp_command(QTestState *who, const char *command, ...); + G_GNUC_PRINTF(3, 4) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index d33e8060f92..71595a74fd2 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -24,6 +24,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "crypto/tlscredspsk.h" +#include "qapi/qmp/qlist.h" #include "migration-helpers.h" #include "tests/migration/migration-test.h" @@ -46,8 +47,11 @@ unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; -/* A downtime where the test really should converge */ -#define CONVERGE_DOWNTIME 1000 +/* + * 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 @@ -402,6 +406,20 @@ static void migrate_set_parameter_str(QTestState *who, const char *parameter, migrate_check_parameter_str(who, parameter, value); } +static void migrate_ensure_non_converge(QTestState *who) +{ + /* Can't converge with 1ms downtime + 30 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 30 * 1000 * 1000); + 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); +} + static void migrate_pause(QTestState *who) { QDict *rsp; @@ -485,6 +503,82 @@ typedef struct { 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 */ + unsigned int iterations; + + /* Postcopy specific fields */ + void *postcopy_data; + bool postcopy_preempt; +} MigrateCommon; + static int test_migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { @@ -971,25 +1065,29 @@ test_migrate_tls_x509_finish(QTestState *from, static int migrate_postcopy_prepare(QTestState **from_ptr, QTestState **to_ptr, - MigrateStart *args) + MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, args)) { + if (test_migrate_start(&from, &to, uri, &args->start)) { return -1; } + if (args->start_hook) { + args->postcopy_data = args->start_hook(from, to); + } + migrate_set_capability(from, "postcopy-ram", true); migrate_set_capability(to, "postcopy-ram", true); migrate_set_capability(to, "postcopy-blocktime", true); - /* We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - migrate_set_parameter_int(from, "max-bandwidth", 30000000); - migrate_set_parameter_int(from, "downtime-limit", 1); + if (args->postcopy_preempt) { + migrate_set_capability(from, "postcopy-preempt", true); + migrate_set_capability(to, "postcopy-preempt", true); + } + + migrate_ensure_non_converge(from); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -1004,7 +1102,8 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, return 0; } -static void migrate_postcopy_complete(QTestState *from, QTestState *to) +static void migrate_postcopy_complete(QTestState *from, QTestState *to, + MigrateCommon *args) { wait_for_migration_complete(from); @@ -1015,30 +1114,73 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to) read_blocktime(to); } + if (args->finish_hook) { + args->finish_hook(from, to, args->postcopy_data); + args->postcopy_data = NULL; + } + test_migrate_end(from, to, true); } -static void test_postcopy(void) +static void test_postcopy_common(MigrateCommon *args) { - MigrateStart args = {}; QTestState *from, *to; - if (migrate_postcopy_prepare(&from, &to, &args)) { + if (migrate_postcopy_prepare(&from, &to, args)) { return; } migrate_postcopy_start(from, to); - migrate_postcopy_complete(from, to); + migrate_postcopy_complete(from, to, args); } -static void test_postcopy_recovery(void) +static void test_postcopy(void) { - MigrateStart args = { - .hide_stderr = true, + MigrateCommon args = { }; + + 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; - if (migrate_postcopy_prepare(&from, &to, &args)) { + /* Always hide errors for postcopy recover tests since they're expected */ + args->start.hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { return; } @@ -1089,8 +1231,50 @@ static void test_postcopy_recovery(void) /* Restore the postcopy bandwidth to unlimited */ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - migrate_postcopy_complete(from, to); + migrate_postcopy_complete(from, to, args); +} + +static void test_postcopy_recovery(void) +{ + MigrateCommon args = { }; + + test_postcopy_recovery_common(&args); +} + +#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) { @@ -1107,78 +1291,6 @@ static void test_baddest(void) test_migrate_end(from, to, false); } -/* - * A hook that runs after the src and dst QEMUs have been - * created, but before the migration is started. This can - * be used to set migration parameters and capabilities. - * - * Returns: NULL, or a pointer to opaque state to be - * later passed to the TestMigrateFinishHook - */ -typedef void * (*TestMigrateStartHook)(QTestState *from, - QTestState *to); - -/* - * 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 */ - unsigned int iterations; -} MigrateCommon; - static void test_precopy_common(MigrateCommon *args) { QTestState *from, *to; @@ -1188,15 +1300,7 @@ static void test_precopy_common(MigrateCommon *args) return; } - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge*/ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + migrate_ensure_non_converge(from); if (args->start_hook) { data_hook = args->start_hook(from, to); @@ -1230,7 +1334,11 @@ static void test_precopy_common(MigrateCommon *args) wait_for_migration_pass(from); } - migrate_set_parameter_int(from, "downtime-limit", CONVERGE_DOWNTIME); + 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_stop) { qtest_qmp_eventwait(from, "STOP"); @@ -1239,7 +1347,6 @@ static void test_precopy_common(MigrateCommon *args) qtest_qmp_eventwait(to, "RESUME"); wait_for_serial("dest_serial"); - wait_for_migration_complete(from); } if (args->finish_hook) { @@ -1691,8 +1798,7 @@ static void test_migrate_auto_converge(void) * Set the initial parameters so that the migration could not converge * without throttling. */ - migrate_set_parameter_int(from, "downtime-limit", 1); - migrate_set_parameter_int(from, "max-bandwidth", 100000000); /* ~100Mb/s */ + migrate_ensure_non_converge(from); /* To check remaining size after precopy */ migrate_set_capability(from, "pause-before-switchover", true); @@ -1997,15 +2103,7 @@ static void test_multifd_tcp_cancel(void) return; } - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge*/ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 300MB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 30000000); + migrate_ensure_non_converge(from); migrate_set_parameter_int(from, "multifd-channels", 16); migrate_set_parameter_int(to, "multifd-channels", 16); @@ -2051,10 +2149,7 @@ static void test_multifd_tcp_cancel(void) wait_for_migration_status(from, "cancelled", NULL); - /* 300ms it should converge */ - migrate_set_parameter_int(from, "downtime-limit", 300); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + migrate_ensure_converge(from); migrate_qmp(from, uri, "{}"); @@ -2070,6 +2165,253 @@ static void test_multifd_tcp_cancel(void) test_migrate_end(from, to2, true); } +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qobject_unref(qmp_command(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %ld," + "'mode': 'dirty-ring' }}", + calc_time)); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qmp_command(who, "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qobject_unref(qmp_command(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %ld } }", + dirtyrate)); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qobject_unref(qmp_command(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) +{ + 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 bool kvm_dirty_ring_supported(void) { #if defined(__linux__) && defined(HOST_X86_64) @@ -2134,13 +2476,31 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); qtest_add_func("/migration/postcopy/unix", test_postcopy); - qtest_add_func("/migration/postcopy/recovery", test_postcopy_recovery); + qtest_add_func("/migration/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); + 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); #ifdef CONFIG_GNUTLS qtest_add_func("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); + /* + * 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); @@ -2215,6 +2575,8 @@ int main(int argc, char **argv) if (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); } ret = g_test_run(); diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index 3fa6d9ece0b..8048044d281 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -50,7 +50,7 @@ #define CON_INT BIT(18) #define CON_EN BIT(17) #define CON_RST BIT(16) -#define CON_CONV BIT(14) +#define CON_CONV BIT(13) #define CON_DIV(rv) extract32(rv, 1, 8) #define FST_RDST BIT(1) diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 056b40e67f6..af007124586 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -110,6 +110,8 @@ static bool query_is_ignored(const char *cmd) "query-sev-capabilities", "query-sgx", "query-sgx-capabilities", + /* Success depends on enabling dirty page rate limit */ + "query-vcpu-dirty-limit", NULL }; int i; diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index e28c71bd8f7..25305a4cf78 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -371,8 +371,15 @@ static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname, return req; } +/* type[1] version[4] path[8] */ typedef char v9fs_qid[13]; +static inline bool is_same_qid(v9fs_qid a, v9fs_qid b) +{ + /* don't compare QID version for checking for file ID equalness */ + return a[0] == b[0] && memcmp(&a[5], &b[5], 8) == 0; +} + /* size[4] Rattach tag[2] qid[13] */ static void v9fs_rattach(P9Req *req, v9fs_qid *qid) { @@ -425,6 +432,79 @@ static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) v9fs_req_free(req); } +/* size[4] Tgetattr tag[2] fid[4] request_mask[8] */ +static P9Req *v9fs_tgetattr(QVirtio9P *v9p, uint32_t fid, uint64_t request_mask, + uint16_t tag) +{ + P9Req *req; + + req = v9fs_req_init(v9p, 4 + 8, P9_TGETATTR, tag); + v9fs_uint32_write(req, fid); + v9fs_uint64_write(req, request_mask); + v9fs_req_send(req); + return req; +} + +typedef struct v9fs_attr { + uint64_t valid; + v9fs_qid qid; + uint32_t mode; + uint32_t uid; + uint32_t gid; + uint64_t nlink; + uint64_t rdev; + uint64_t size; + uint64_t blksize; + uint64_t blocks; + uint64_t atime_sec; + uint64_t atime_nsec; + uint64_t mtime_sec; + uint64_t mtime_nsec; + uint64_t ctime_sec; + uint64_t ctime_nsec; + uint64_t btime_sec; + uint64_t btime_nsec; + uint64_t gen; + uint64_t data_version; +} v9fs_attr; + +#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ + +/* + * size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8] + * rdev[8] size[8] blksize[8] blocks[8] + * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] + * ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] + * gen[8] data_version[8] + */ +static void v9fs_rgetattr(P9Req *req, v9fs_attr *attr) +{ + v9fs_req_recv(req, P9_RGETATTR); + + v9fs_uint64_read(req, &attr->valid); + v9fs_memread(req, &attr->qid, 13); + v9fs_uint32_read(req, &attr->mode); + v9fs_uint32_read(req, &attr->uid); + v9fs_uint32_read(req, &attr->gid); + v9fs_uint64_read(req, &attr->nlink); + v9fs_uint64_read(req, &attr->rdev); + v9fs_uint64_read(req, &attr->size); + v9fs_uint64_read(req, &attr->blksize); + v9fs_uint64_read(req, &attr->blocks); + v9fs_uint64_read(req, &attr->atime_sec); + v9fs_uint64_read(req, &attr->atime_nsec); + v9fs_uint64_read(req, &attr->mtime_sec); + v9fs_uint64_read(req, &attr->mtime_nsec); + v9fs_uint64_read(req, &attr->ctime_sec); + v9fs_uint64_read(req, &attr->ctime_nsec); + v9fs_uint64_read(req, &attr->btime_sec); + v9fs_uint64_read(req, &attr->btime_nsec); + v9fs_uint64_read(req, &attr->gen); + v9fs_uint64_read(req, &attr->data_version); + + v9fs_req_free(req); +} + /* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ static P9Req *v9fs_treaddir(QVirtio9P *v9p, uint32_t fid, uint64_t offset, uint32_t count, uint16_t tag) @@ -589,21 +669,50 @@ static void do_version(QVirtio9P *v9p) g_assert_cmpmem(server_version, server_len, version, strlen(version)); } +/* + * utility function: walk to requested dir and return fid for that dir and + * the QIDs of server response + */ +static uint32_t do_walk_rqids(QVirtio9P *v9p, const char *path, uint16_t *nwqid, + v9fs_qid **wqid) +{ + char **wnames; + P9Req *req; + const uint32_t fid = genfid(); + + int nwnames = split(path, "/", &wnames); + + req = v9fs_twalk(v9p, 0, fid, nwnames, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, nwqid, wqid); + + split_free(&wnames); + return fid; +} + /* utility function: walk to requested dir and return fid for that dir */ static uint32_t do_walk(QVirtio9P *v9p, const char *path) +{ + return do_walk_rqids(v9p, path, NULL, NULL); +} + +/* utility function: walk to requested dir and expect passed error response */ +static void do_walk_expect_error(QVirtio9P *v9p, const char *path, uint32_t err) { char **wnames; P9Req *req; + uint32_t _err; const uint32_t fid = genfid(); int nwnames = split(path, "/", &wnames); req = v9fs_twalk(v9p, 0, fid, nwnames, wnames, 0); v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); + v9fs_rlerror(req, &_err); + + g_assert_cmpint(_err, ==, err); split_free(&wnames); - return fid; } static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) @@ -612,14 +721,19 @@ static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) do_version(obj); } -static void do_attach(QVirtio9P *v9p) +static void do_attach_rqid(QVirtio9P *v9p, v9fs_qid *qid) { P9Req *req; do_version(v9p); req = v9fs_tattach(v9p, 0, getuid(), 0); v9fs_req_wait_for_reply(req, NULL); - v9fs_rattach(req, NULL); + v9fs_rattach(req, qid); +} + +static void do_attach(QVirtio9P *v9p) +{ + do_attach_rqid(v9p, NULL); } static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc) @@ -974,6 +1088,80 @@ static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) g_free(wnames[0]); } +static void fs_walk_nonexistent(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + + do_attach(v9p); + /* + * The 9p2000 protocol spec says: "If the first element cannot be walked + * for any reason, Rerror is returned." + */ + do_walk_expect_error(v9p, "non-existent", ENOENT); +} + +static void fs_walk_2nd_nonexistent(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + v9fs_qid root_qid; + uint16_t nwqid; + uint32_t fid, err; + P9Req *req; + g_autofree v9fs_qid *wqid = NULL; + g_autofree char *path = g_strdup_printf( + QTEST_V9FS_SYNTH_WALK_FILE "/non-existent", 0 + ); + + do_attach_rqid(v9p, &root_qid); + fid = do_walk_rqids(v9p, path, &nwqid, &wqid); + /* + * The 9p2000 protocol spec says: "nwqid is therefore either nwname or the + * index of the first elementwise walk that failed." + */ + assert(nwqid == 1); + + /* returned QID wqid[0] is file ID of 1st subdir */ + g_assert(wqid && wqid[0] && !is_same_qid(root_qid, wqid[0])); + + /* expect fid being unaffected by walk above */ + req = v9fs_tgetattr(v9p, fid, P9_GETATTR_BASIC, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlerror(req, &err); + + g_assert_cmpint(err, ==, ENOENT); +} + +static void fs_walk_none(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + v9fs_qid root_qid; + g_autofree v9fs_qid *wqid = NULL; + P9Req *req; + struct v9fs_attr attr; + + do_version(v9p); + req = v9fs_tattach(v9p, 0, getuid(), 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rattach(req, &root_qid); + + req = v9fs_twalk(v9p, 0, 1, 0, NULL, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, NULL, &wqid); + + /* special case: no QID is returned if nwname=0 was sent */ + g_assert(wqid == NULL); + + req = v9fs_tgetattr(v9p, 1, P9_GETATTR_BASIC, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rgetattr(req, &attr); + + g_assert(is_same_qid(root_qid, attr.qid)); +} + static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; @@ -1407,8 +1595,13 @@ static void register_virtio_9p_test(void) qos_add_test("synth/walk/basic", "virtio-9p", fs_walk, &opts); qos_add_test("synth/walk/no_slash", "virtio-9p", fs_walk_no_slash, &opts); + qos_add_test("synth/walk/none", "virtio-9p", fs_walk_none, &opts); qos_add_test("synth/walk/dotdot_from_root", "virtio-9p", fs_walk_dotdot, &opts); + qos_add_test("synth/walk/non_existent", "virtio-9p", fs_walk_nonexistent, + &opts); + qos_add_test("synth/walk/2nd_non_existent", "virtio-9p", + fs_walk_2nd_nonexistent, &opts); qos_add_test("synth/lopen/basic", "virtio-9p", fs_lopen, &opts); qos_add_test("synth/write/basic", "virtio-9p", fs_write, &opts); qos_add_test("synth/flush/success", "virtio-9p", fs_flush_success, diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index f427a0304e9..e68830af15b 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -111,6 +111,7 @@ else # For softmmu targets we include a different Makefile fragement as the # build options for bare programs are usually pretty different. They # are expected to provide their own build recipes. +EXTRA_CFLAGS += -ffreestanding -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 diff --git a/tests/tcg/aarch64/system/pauth-3.c b/tests/tcg/aarch64/system/pauth-3.c index 42eff4d5eae..77a467277bc 100644 --- a/tests/tcg/aarch64/system/pauth-3.c +++ b/tests/tcg/aarch64/system/pauth-3.c @@ -1,4 +1,4 @@ -#include +#include #include int main() diff --git a/tests/tcg/aarch64/system/semiconsole.c b/tests/tcg/aarch64/system/semiconsole.c index bfe7c9e26b4..81324c639f7 100644 --- a/tests/tcg/aarch64/system/semiconsole.c +++ b/tests/tcg/aarch64/system/semiconsole.c @@ -6,7 +6,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#include +#include #include #define SYS_READC 0x7 diff --git a/tests/tcg/aarch64/system/semiheap.c b/tests/tcg/aarch64/system/semiheap.c index 4ed258476d5..a254bd8982c 100644 --- a/tests/tcg/aarch64/system/semiheap.c +++ b/tests/tcg/aarch64/system/semiheap.c @@ -6,7 +6,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#include +#include #include #include diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 23b98705349..96a4d7a614d 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -35,6 +35,7 @@ HEX_TESTS += preg_alias HEX_TESTS += dual_stores HEX_TESTS += multi_result HEX_TESTS += mem_noshuf +HEX_TESTS += mem_noshuf_exception HEX_TESTS += circ HEX_TESTS += brev HEX_TESTS += load_unpack diff --git a/tests/tcg/hexagon/mem_noshuf.c b/tests/tcg/hexagon/mem_noshuf.c index dd714d5e98a..0f4064e700b 100644 --- a/tests/tcg/hexagon/mem_noshuf.c +++ b/tests/tcg/hexagon/mem_noshuf.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -84,6 +84,70 @@ MEM_NOSHUF32(mem_noshuf_sd_luh, long long, unsigned short, memd, memuh) MEM_NOSHUF32(mem_noshuf_sd_lw, long long, signed int, memd, memw) MEM_NOSHUF64(mem_noshuf_sd_ld, long long, signed long long, memd, memd) +static inline int pred_lw_sw(int pred, int *p, int *q, int x, int y) +{ + int ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "{\n\t" + " memw(%1) = %4\n\t" + " if (!p0) %0 = memw(%2)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "p0", "memory"); + return ret; +} + +static inline int pred_lw_sw_pi(int pred, int *p, int *q, int x, int y) +{ + int ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "r7 = %2\n\t" + "{\n\t" + " memw(%1) = %4\n\t" + " if (!p0) %0 = memw(r7++#4)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "r7", "p0", "memory"); + return ret; +} + +static inline long long pred_ld_sd(int pred, long long *p, long long *q, + long long x, long long y) +{ + unsigned long long ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "{\n\t" + " memd(%1) = %4\n\t" + " if (!p0) %0 = memd(%2)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "p0", "memory"); + return ret; +} + +static inline long long pred_ld_sd_pi(int pred, long long *p, long long *q, + long long x, long long y) +{ + long long ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "r7 = %2\n\t" + "{\n\t" + " memd(%1) = %4\n\t" + " if (!p0) %0 = memd(r7++#8)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "p0", "memory"); + return ret; +} + static inline unsigned int cancel_sw_lb(int pred, int *p, signed char *q, int x) { unsigned int ret; @@ -126,18 +190,22 @@ typedef union { int err; -static void check32(int n, int expect) +#define check32(n, expect) check32_(n, expect, __LINE__) + +static void check32_(int n, int expect, int line) { if (n != expect) { - printf("ERROR: 0x%08x != 0x%08x\n", n, expect); + printf("ERROR: 0x%08x != 0x%08x, line %d\n", n, expect, line); err++; } } -static void check64(long long n, long long expect) +#define check64(n, expect) check64_(n, expect, __LINE__) + +static void check64_(long long n, long long expect, int line) { if (n != expect) { - printf("ERROR: 0x%08llx != 0x%08llx\n", n, expect); + printf("ERROR: 0x%08llx != 0x%08llx, line %d\n", n, expect, line); err++; } } @@ -323,6 +391,50 @@ int main() res64 = mem_noshuf_sd_ld(&n.d[0], &n.d[1], 0x123456789abcdef0LL); check64(res64, 0xffffffffffffffffLL); + n.w[0] = ~0; + res32 = pred_lw_sw(0, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0x12345678); + check32(n.w[0], 0xc0ffeeda); + + n.w[0] = ~0; + res32 = pred_lw_sw(1, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0xc0ffeeda); + check32(n.w[0], 0xc0ffeeda); + + n.w[0] = ~0; + res32 = pred_lw_sw_pi(0, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0x12345678); + check32(n.w[0], 0xc0ffeeda); + + n.w[0] = ~0; + res32 = pred_lw_sw_pi(1, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0xc0ffeeda); + check32(n.w[0], 0xc0ffeeda); + + n.d[0] = ~0LL; + res64 = pred_ld_sd(0, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0x1234567812345678LL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + + n.d[0] = ~0LL; + res64 = pred_ld_sd(1, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0xc0ffeedac0ffeedaLL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + + n.d[0] = ~0LL; + res64 = pred_ld_sd_pi(0, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0x1234567812345678LL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + + n.d[0] = ~0LL; + res64 = pred_ld_sd_pi(1, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0xc0ffeedac0ffeedaLL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + puts(err ? "FAIL" : "PASS"); return err; } diff --git a/tests/tcg/hexagon/mem_noshuf_exception.c b/tests/tcg/hexagon/mem_noshuf_exception.c new file mode 100644 index 00000000000..08660ea3e15 --- /dev/null +++ b/tests/tcg/hexagon/mem_noshuf_exception.c @@ -0,0 +1,146 @@ +/* + * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Test the VLIW semantics of exceptions with mem_noshuf + * + * When a packet has the :mem_noshuf attribute, the semantics dictate + * That the load will get the data from the store if the addresses overlap. + * To accomplish this, we perform the store first. However, we have to + * handle the case where the store raises an exception. In that case, the + * store should not alter the machine state. + * + * We test this with a mem_noshuf packet with a store to a global variable, + * "should_not_change" and a load from NULL. After the SIGSEGV is caught, + * we check * that the "should_not_change" value is the same. + * + * We also check that a predicated load where the predicate is false doesn't + * raise an exception and allows the store to happen. + */ + +#include +#include +#include +#include +#include +#include +#include + +int err; +int segv_caught; + +#define SHOULD_NOT_CHANGE_VAL 5 +int should_not_change = SHOULD_NOT_CHANGE_VAL; + +#define OK_TO_CHANGE_VAL 13 +int ok_to_change = OK_TO_CHANGE_VAL; + +static void __check(const char *filename, int line, int x, int expect) +{ + if (x != expect) { + printf("ERROR %s:%d - %d != %d\n", + filename, line, x, expect); + err++; + } +} + +#define check(x, expect) __check(__FILE__, __LINE__, (x), (expect)) + +static void __chk_error(const char *filename, int line, int ret) +{ + if (ret < 0) { + printf("ERROR %s:%d - %d\n", filename, line, ret); + err++; + } +} + +#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) + +jmp_buf jmp_env; + +static void sig_segv(int sig, siginfo_t *info, void *puc) +{ + check(sig, SIGSEGV); + segv_caught = 1; + longjmp(jmp_env, 1); +} + +int main() +{ + struct sigaction act; + int dummy32; + long long dummy64; + void *p; + + /* SIGSEGV test */ + act.sa_sigaction = sig_segv; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGSEGV, &act, NULL)); + if (setjmp(jmp_env) == 0) { + asm volatile("r18 = ##should_not_change\n\t" + "r19 = #0\n\t" + "{\n\t" + " memw(r18) = #7\n\t" + " %0 = memw(r19)\n\t" + "}:mem_noshuf\n\t" + : "=r"(dummy32) : : "r18", "r19", "memory"); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGSEGV, &act, NULL)); + + check(segv_caught, 1); + check(should_not_change, SHOULD_NOT_CHANGE_VAL); + + /* + * Check that a predicated load where the predicate is false doesn't + * raise an exception and allows the store to happen. + */ + asm volatile("r18 = ##ok_to_change\n\t" + "r19 = #0\n\t" + "p0 = cmp.gt(r0, r0)\n\t" + "{\n\t" + " memw(r18) = #7\n\t" + " if (p0) %0 = memw(r19)\n\t" + "}:mem_noshuf\n\t" + : "=r"(dummy32) : : "r18", "r19", "p0", "memory"); + + check(ok_to_change, 7); + + /* + * Also check that the post-increment doesn't happen when the + * predicate is false. + */ + ok_to_change = OK_TO_CHANGE_VAL; + p = NULL; + asm volatile("r18 = ##ok_to_change\n\t" + "p0 = cmp.gt(r0, r0)\n\t" + "{\n\t" + " memw(r18) = #9\n\t" + " if (p0) %1 = memd(%0 ++ #8)\n\t" + "}:mem_noshuf\n\t" + : "+r"(p), "=r"(dummy64) : : "r18", "p0", "memory"); + + check(ok_to_change, 9); + check((int)p, (int)NULL); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/tests/tcg/loongarch64/Makefile.target b/tests/tcg/loongarch64/Makefile.target new file mode 100644 index 00000000000..0115de78eff --- /dev/null +++ b/tests/tcg/loongarch64/Makefile.target @@ -0,0 +1,19 @@ +# -*- Mode: makefile -*- +# +# LoongArch64 specific tweaks + +# Loongarch64 doesn't support gdb, so skip the EXTRA_RUNS +EXTRA_RUNS = + +LOONGARCH64_SRC=$(SRC_PATH)/tests/tcg/loongarch64 +VPATH += $(LOONGARCH64_SRC) + +LDFLAGS+=-lm + +LOONGARCH64_TESTS = test_bit +LOONGARCH64_TESTS += test_div +LOONGARCH64_TESTS += test_fclass +LOONGARCH64_TESTS += test_fpcom +LOONGARCH64_TESTS += test_pcadd + +TESTS += $(LOONGARCH64_TESTS) diff --git a/tests/tcg/loongarch64/float_convd.ref b/tests/tcg/loongarch64/float_convd.ref new file mode 100644 index 00000000000..08d3dfa2fe6 --- /dev/null +++ b/tests/tcg/loongarch64/float_convd.ref @@ -0,0 +1,988 @@ +### Rounding to nearest +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding upwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000200000000000000p-25:0x33000001) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000e00000000000000p-14:0x38800007) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding downwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x1.00000000000000000000p-149:0x80000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding to zero +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) diff --git a/tests/tcg/loongarch64/float_convs.ref b/tests/tcg/loongarch64/float_convs.ref new file mode 100644 index 00000000000..66c7679dec6 --- /dev/null +++ b/tests/tcg/loongarch64/float_convs.ref @@ -0,0 +1,748 @@ +### Rounding to nearest +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding upwards +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding downwards +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding to zero +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) diff --git a/tests/tcg/loongarch64/float_madds.ref b/tests/tcg/loongarch64/float_madds.ref new file mode 100644 index 00000000000..21c05398878 --- /dev/null +++ b/tests/tcg/loongarch64/float_madds.ref @@ -0,0 +1,768 @@ +### Rounding to nearest +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27fa00000000000000p+60:0x5d8613fd) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46200000000000000p+34:0x50936231) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f94000000000000000p-106:0x0ac8fca0) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f75000000000000000p-40:0xab98fba8) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804200000000000000p+3:0x41094021) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3c00000000000000p+17:0x4848f69e) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edf000000000000000p+18:0x488476f8) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7a00000000000000p+18:0x4884773d) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840800000000000000p+31:0x4f7fc204) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+31:0x4f7fc104) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860800000000000000p+31:0x4f7fc304) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+32:0x4fffc104) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830800000000000000p+32:0x4fffc184) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840800000000000000p+32:0x4fffc204) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820800000000000000p+33:0x507fc104) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810800000000000000p+33:0x507fc084) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0838000000000000000p+116:0x79e041c0) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0) +### Rounding upwards +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27fa00000000000000p+60:0x5d8613fd) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46200000000000000p+34:0x50936231) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f94000000000000000p-106:0x0ac8fca0) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f74e00000000000000p-40:0xab98fba7) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544200000000000000p-66:0x9ea82a21) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe800000000000000p-25:0x337ffff4) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe800000000000000p-50:0x26fffff4) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000200000000000000p-25:0x33000001) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00080000000000000000p-25:0x33000400) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f400000000000000p-24:0x338000fa) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000e00000000000000p-14:0x38800007) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf600000000000000p-24:0x3387fdfb) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000200000000000000p+0:0x3f800001) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01a00000000000000p-14:0x38ffe00d) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01a00000000000000p-14:0x38ffe00d) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440200000000000000p+0:0x3f802201) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440200000000000000p+0:0x3f802201) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d400000000000000p+2:0x409711ea) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804200000000000000p+3:0x41094021) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458200000000000000p+3:0x4128a2c1) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0600000000000000p+3:0x41100603) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1600000000000000p+15:0x477fe78b) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3c00000000000000p+17:0x4848f69e) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56200000000000000p+17:0x482de2b1) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edf000000000000000p+18:0x488476f8) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0a00000000000000p+31:0x4f7fbf05) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7a00000000000000p+18:0x4884773d) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800a00000000000000p+31:0x4f7fc005) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840800000000000000p+31:0x4f7fc204) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+31:0x4f7fc104) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860800000000000000p+31:0x4f7fc304) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+32:0x4fffc104) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800a00000000000000p+32:0x4fffc005) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830800000000000000p+32:0x4fffc184) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8a00000000000000p+33:0x507fbfc5) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840800000000000000p+32:0x4fffc204) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800a00000000000000p+33:0x507fc005) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820800000000000000p+33:0x507fc104) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810800000000000000p+33:0x507fc084) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab800000000000000p+99:0x71605d5c) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0838000000000000000p+116:0x79e041c0) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c082a000000000000000p+116:0x79e04150) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-148:0x00000002) flags=UNDERFLOW INEXACT (32/0) +### Rounding downwards +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27f800000000000000p+60:0x5d8613fc) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46000000000000000p+34:0x50936230) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f93e00000000000000p-106:0x0ac8fc9f) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f75000000000000000p-40:0xab98fba8) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x1.00000000000000000000p-149:0x80000001) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804000000000000000p+3:0x41094020) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3a00000000000000p+17:0x4848f69d) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edee00000000000000p+18:0x488476f7) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7800000000000000p+18:0x4884773c) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840600000000000000p+31:0x4f7fc203) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+31:0x4f7fc103) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860600000000000000p+31:0x4f7fc303) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+32:0x4fffc103) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830600000000000000p+32:0x4fffc183) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840600000000000000p+32:0x4fffc203) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820600000000000000p+33:0x507fc103) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810600000000000000p+33:0x507fc083) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0837e00000000000000p+116:0x79e041bf) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0) +### Rounding to zero +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27f800000000000000p+60:0x5d8613fc) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46000000000000000p+34:0x50936230) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f93e00000000000000p-106:0x0ac8fc9f) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f74e00000000000000p-40:0xab98fba7) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544200000000000000p-66:0x9ea82a21) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804000000000000000p+3:0x41094020) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3a00000000000000p+17:0x4848f69d) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edee00000000000000p+18:0x488476f7) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7800000000000000p+18:0x4884773c) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840600000000000000p+31:0x4f7fc203) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+31:0x4f7fc103) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860600000000000000p+31:0x4f7fc303) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+32:0x4fffc103) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830600000000000000p+32:0x4fffc183) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840600000000000000p+32:0x4fffc203) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820600000000000000p+33:0x507fc103) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810600000000000000p+33:0x507fc083) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0837e00000000000000p+116:0x79e041bf) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0) diff --git a/tests/tcg/loongarch64/test_bit.c b/tests/tcg/loongarch64/test_bit.c new file mode 100644 index 00000000000..a6d9904909e --- /dev/null +++ b/tests/tcg/loongarch64/test_bit.c @@ -0,0 +1,88 @@ +#include +#include + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X))) +#define TEST_CLO(N) \ +static uint64_t test_clo_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("clo."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +#define TEST_CLZ(N) \ +static uint64_t test_clz_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("clz."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +#define TEST_CTO(N) \ +static uint64_t test_cto_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("cto."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +#define TEST_CTZ(N) \ +static uint64_t test_ctz_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("ctz."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +TEST_CLO(w) +TEST_CLO(d) +TEST_CLZ(w) +TEST_CLZ(d) +TEST_CTO(w) +TEST_CTO(d) +TEST_CTZ(w) +TEST_CTZ(d) + +struct vector { + uint64_t (*func)(uint64_t); + uint64_t u; + uint64_t r; +}; + +static struct vector vectors[] = { + {test_clo_w, 0xfff11fff392476ab, 0}, + {test_clo_d, 0xabd28a64000000, 0}, + {test_clz_w, 0xfaffff42392476ab, 2}, + {test_clz_d, 0xabd28a64000000, 8}, + {test_cto_w, 0xfff11fff392476ab, 2}, + {test_cto_d, 0xabd28a64000000, 0}, + {test_ctz_w, 0xfaffff42392476ab, 0}, + {test_ctz_d, 0xabd28a64000000, 26}, +}; + +int main() +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vectors); i++) { + assert((*vectors[i].func)(vectors[i].u) == vectors[i].r); + } + + return 0; +} diff --git a/tests/tcg/loongarch64/test_div.c b/tests/tcg/loongarch64/test_div.c new file mode 100644 index 00000000000..6c31fe97aeb --- /dev/null +++ b/tests/tcg/loongarch64/test_div.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#define TEST_DIV(N, M) \ +static void test_div_ ##N(uint ## M ## _t rj, \ + uint ## M ## _t rk, \ + uint64_t rm) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("div."#N" %0,%1,%2\n\t" \ + : "=r"(rd) \ + : "r"(rj), "r"(rk) \ + : ); \ + assert(rd == rm); \ +} + +#define TEST_MOD(N, M) \ +static void test_mod_ ##N(uint ## M ## _t rj, \ + uint ## M ## _t rk, \ + uint64_t rm) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("mod."#N" %0,%1,%2\n\t" \ + : "=r"(rd) \ + : "r"(rj), "r"(rk) \ + : ); \ + assert(rd == rm); \ +} + +TEST_DIV(w, 32) +TEST_DIV(wu, 32) +TEST_DIV(d, 64) +TEST_DIV(du, 64) +TEST_MOD(w, 32) +TEST_MOD(wu, 32) +TEST_MOD(d, 64) +TEST_MOD(du, 64) + +int main(void) +{ + test_div_w(0xffaced97, 0xc36abcde, 0x0); + test_div_wu(0xffaced97, 0xc36abcde, 0x1); + test_div_d(0xffaced973582005f, 0xef56832a358b, 0xffffffffffffffa8); + test_div_du(0xffaced973582005f, 0xef56832a358b, 0x11179); + test_mod_w(0x7cf18c32, 0xa04da650, 0x1d3f3282); + test_mod_wu(0x7cf18c32, 0xc04da650, 0x7cf18c32); + test_mod_d(0x7cf18c3200000000, 0xa04da65000000000, 0x1d3f328200000000); + test_mod_du(0x7cf18c3200000000, 0xc04da65000000000, 0x7cf18c3200000000); + + return 0; +} diff --git a/tests/tcg/loongarch64/test_fclass.c b/tests/tcg/loongarch64/test_fclass.c new file mode 100644 index 00000000000..7ba1d2c1510 --- /dev/null +++ b/tests/tcg/loongarch64/test_fclass.c @@ -0,0 +1,130 @@ +#include + +/* float class */ +#define FLOAT_CLASS_SIGNALING_NAN 0x001 +#define FLOAT_CLASS_QUIET_NAN 0x002 +#define FLOAT_CLASS_NEGATIVE_INFINITY 0x004 +#define FLOAT_CLASS_NEGATIVE_NORMAL 0x008 +#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 +#define FLOAT_CLASS_NEGATIVE_ZERO 0x020 +#define FLOAT_CLASS_POSITIVE_INFINITY 0x040 +#define FLOAT_CLASS_POSITIVE_NORMAL 0x080 +#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 +#define FLOAT_CLASS_POSITIVE_ZERO 0x200 + +#define TEST_FCLASS(N) \ +void test_fclass_##N(long s) \ +{ \ + double fd; \ + long rd; \ + \ + asm volatile("fclass."#N" %0, %2\n\t" \ + "movfr2gr."#N" %1, %2\n\t" \ + : "=f"(fd), "=r"(rd) \ + : "f"(s) \ + : ); \ + switch (rd) { \ + case FLOAT_CLASS_SIGNALING_NAN: \ + case FLOAT_CLASS_QUIET_NAN: \ + case FLOAT_CLASS_NEGATIVE_INFINITY: \ + case FLOAT_CLASS_NEGATIVE_NORMAL: \ + case FLOAT_CLASS_NEGATIVE_SUBNORMAL: \ + case FLOAT_CLASS_NEGATIVE_ZERO: \ + case FLOAT_CLASS_POSITIVE_INFINITY: \ + case FLOAT_CLASS_POSITIVE_NORMAL: \ + case FLOAT_CLASS_POSITIVE_SUBNORMAL: \ + case FLOAT_CLASS_POSITIVE_ZERO: \ + break; \ + default: \ + printf("fclass."#N" test failed.\n"); \ + break; \ + } \ +} + +/* + * float format + * type | S | Exponent | Fraction | example value + * 31 | 30 --23 | 22 | 21 --0 | + * | bit | + * SNAN 0/1 | 0xFF | 0 | !=0 | 0x7FBFFFFF + * QNAN 0/1 | 0xFF | 1 | | 0x7FCFFFFF + * -infinity 1 | 0xFF | 0 | 0xFF800000 + * -normal 1 | [1, 0xFE] | [0, 0x7FFFFF]| 0xFF7FFFFF + * -subnormal 1 | 0 | !=0 | 0x807FFFFF + * -0 1 | 0 | 0 | 0x80000000 + * +infinity 0 | 0xFF | 0 | 0x7F800000 + * +normal 0 | [1, 0xFE] | [0, 0x7FFFFF]| 0x7F7FFFFF + * +subnormal 0 | 0 | !=0 | 0x007FFFFF + * +0 0 | 0 | 0 | 0x00000000 + */ + +long float_snan = 0x7FBFFFFF; +long float_qnan = 0x7FCFFFFF; +long float_neg_infinity = 0xFF800000; +long float_neg_normal = 0xFF7FFFFF; +long float_neg_subnormal = 0x807FFFFF; +long float_neg_zero = 0x80000000; +long float_post_infinity = 0x7F800000; +long float_post_normal = 0x7F7FFFFF; +long float_post_subnormal = 0x007FFFFF; +long float_post_zero = 0x00000000; + +/* + * double format + * type | S | Exponent | Fraction | example value + * 63 | 62 -- 52 | 51 | 50 -- 0 | + * | bit | + * SNAN 0/1 | 0x7FF | 0 | !=0 | 0x7FF7FFFFFFFFFFFF + * QNAN 0/1 | 0x7FF | 1 | | 0x7FFFFFFFFFFFFFFF + * -infinity 1 | 0x7FF | 0 | 0xFFF0000000000000 + * -normal 1 |[1, 0x7FE] | | 0xFFEFFFFFFFFFFFFF + * -subnormal 1 | 0 | !=0 | 0x8007FFFFFFFFFFFF + * -0 1 | 0 | 0 | 0x8000000000000000 + * +infinity 0 | 0x7FF | 0 | 0x7FF0000000000000 + * +normal 0 |[1, 0x7FE] | | 0x7FEFFFFFFFFFFFFF + * +subnormal 0 | 0 | !=0 | 0x000FFFFFFFFFFFFF + * +0 0 | 0 | 0 | 0x0000000000000000 + */ + +long double_snan = 0x7FF7FFFFFFFFFFFF; +long double_qnan = 0x7FFFFFFFFFFFFFFF; +long double_neg_infinity = 0xFFF0000000000000; +long double_neg_normal = 0xFFEFFFFFFFFFFFFF; +long double_neg_subnormal = 0x8007FFFFFFFFFFFF; +long double_neg_zero = 0x8000000000000000; +long double_post_infinity = 0x7FF0000000000000; +long double_post_normal = 0x7FEFFFFFFFFFFFFF; +long double_post_subnormal = 0x000FFFFFFFFFFFFF; +long double_post_zero = 0x0000000000000000; + +TEST_FCLASS(s) +TEST_FCLASS(d) + +int main() +{ + /* fclass.s */ + test_fclass_s(float_snan); + test_fclass_s(float_qnan); + test_fclass_s(float_neg_infinity); + test_fclass_s(float_neg_normal); + test_fclass_s(float_neg_subnormal); + test_fclass_s(float_neg_zero); + test_fclass_s(float_post_infinity); + test_fclass_s(float_post_normal); + test_fclass_s(float_post_subnormal); + test_fclass_s(float_post_zero); + + /* fclass.d */ + test_fclass_d(double_snan); + test_fclass_d(double_qnan); + test_fclass_d(double_neg_infinity); + test_fclass_d(double_neg_normal); + test_fclass_d(double_neg_subnormal); + test_fclass_d(double_neg_zero); + test_fclass_d(double_post_infinity); + test_fclass_d(double_post_normal); + test_fclass_d(double_post_subnormal); + test_fclass_d(double_post_zero); + + return 0; +} diff --git a/tests/tcg/loongarch64/test_fpcom.c b/tests/tcg/loongarch64/test_fpcom.c new file mode 100644 index 00000000000..9e81f767f93 --- /dev/null +++ b/tests/tcg/loongarch64/test_fpcom.c @@ -0,0 +1,37 @@ +#include + +#define TEST_COMP(N) \ +void test_##N(float fj, float fk) \ +{ \ + int rd = 0; \ + \ + asm volatile("fcmp."#N".s $fcc6,%1,%2\n" \ + "movcf2gr %0, $fcc6\n" \ + : "=r"(rd) \ + : "f"(fj), "f"(fk) \ + : ); \ + assert(rd == 1); \ +} + +TEST_COMP(ceq) +TEST_COMP(clt) +TEST_COMP(cle) +TEST_COMP(cne) +TEST_COMP(seq) +TEST_COMP(slt) +TEST_COMP(sle) +TEST_COMP(sne) + +int main() +{ + test_ceq(0xff700102, 0xff700102); + test_clt(0x00730007, 0xff730007); + test_cle(0xff70130a, 0xff70130b); + test_cne(0x1238acde, 0xff71111f); + test_seq(0xff766618, 0xff766619); + test_slt(0xff78881c, 0xff78901d); + test_sle(0xff780b22, 0xff790b22); + test_sne(0xff7bcd25, 0xff7a26cf); + + return 0; +} diff --git a/tests/tcg/loongarch64/test_pcadd.c b/tests/tcg/loongarch64/test_pcadd.c new file mode 100644 index 00000000000..da2a64db827 --- /dev/null +++ b/tests/tcg/loongarch64/test_pcadd.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#define TEST_PCADDU(N) \ +void test_##N(int a) \ +{ \ + uint64_t rd1 = 0; \ + uint64_t rd2 = 0; \ + uint64_t rm, rn; \ + \ + asm volatile(""#N" %0, 0x104\n\t" \ + ""#N" %1, 0x12345\n\t" \ + : "=r"(rd1), "=r"(rd2) \ + : ); \ + rm = rd2 - rd1; \ + if (!strcmp(#N, "pcalau12i")) { \ + rn = ((0x12345UL - 0x104) << a) & ~0xfff; \ + } else { \ + rn = ((0x12345UL - 0x104) << a) + 4; \ + } \ + assert(rm == rn); \ +} + +TEST_PCADDU(pcaddi) +TEST_PCADDU(pcaddu12i) +TEST_PCADDU(pcaddu18i) +TEST_PCADDU(pcalau12i) + +int main() +{ + test_pcaddi(2); + test_pcaddu12i(12); + test_pcaddu18i(18); + test_pcalau12i(12); + + return 0; +} diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/system/memory.c index 41c7f66e2ed..214f7d4f54b 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -12,7 +12,7 @@ * - sign extension when loading */ -#include +#include #include #include diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index babd209573d..331fae628e2 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -11,6 +11,7 @@ endif $(PPC64_TESTS): CFLAGS += -mpower8-vector PPC64_TESTS += mtfsf +PPC64_TESTS += mffsce ifneq ($(CROSS_CC_HAS_POWER10),) PPC64_TESTS += byte_reverse sha512-vector diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index 5b0eb5e8703..6ca3003f026 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -24,6 +24,7 @@ run-sha512-vector: QEMU_OPTS+=-cpu POWER10 run-plugin-sha512-vector-with-%: QEMU_OPTS+=-cpu POWER10 PPC64LE_TESTS += mtfsf +PPC64LE_TESTS += mffsce PPC64LE_TESTS += signal_save_restore_xer PPC64LE_TESTS += xxspltw diff --git a/tests/tcg/ppc64le/mffsce.c b/tests/tcg/ppc64le/mffsce.c new file mode 100644 index 00000000000..20d882cb456 --- /dev/null +++ b/tests/tcg/ppc64le/mffsce.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#define MTFSF(FLM, FRB) asm volatile ("mtfsf %0, %1" :: "i" (FLM), "f" (FRB)) +#define MFFS(FRT) asm("mffs %0" : "=f" (FRT)) +#define MFFSCE(FRT) asm("mffsce %0" : "=f" (FRT)) + +#define PPC_BIT_NR(nr) (63 - (nr)) + +#define FP_VE (1ull << PPC_BIT_NR(56)) +#define FP_UE (1ull << PPC_BIT_NR(58)) +#define FP_ZE (1ull << PPC_BIT_NR(59)) +#define FP_XE (1ull << PPC_BIT_NR(60)) +#define FP_NI (1ull << PPC_BIT_NR(61)) +#define FP_RN1 (1ull << PPC_BIT_NR(63)) + +int main(void) +{ + uint64_t frt, fpscr; + uint64_t test_value = FP_VE | FP_UE | FP_ZE | + FP_XE | FP_NI | FP_RN1; + MTFSF(0b11111111, test_value); /* set test value to cpu fpscr */ + MFFSCE(frt); + MFFS(fpscr); /* read the value that mffsce stored to cpu fpscr */ + + /* the returned value should be as the cpu fpscr was before */ + assert((frt & 0xff) == test_value); + + /* + * the cpu fpscr last 3 bits should be unchanged + * and enable bits should be unset + */ + assert((fpscr & 0xff) == (test_value & 0x7)); + + return 0; +} diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target new file mode 100644 index 00000000000..e22cdb34c55 --- /dev/null +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -0,0 +1,21 @@ +# +# RISC-V system tests +# + +TEST_SRC = $(SRC_PATH)/tests/tcg/riscv64 +VPATH += $(TEST_SRC) + +LINK_SCRIPT = $(TEST_SRC)/semihost.ld +LDFLAGS = -T $(LINK_SCRIPT) +CFLAGS += -g -Og + +%.o: %.S + $(CC) $(CFLAGS) $< -c -o $@ +%: %.o $(LINK_SCRIPT) + $(LD) $(LDFLAGS) $< -o $@ + +QEMU_OPTS += -M virt -display none -semihosting -device loader,file= + +EXTRA_RUNS += run-issue1060 +run-issue1060: issue1060 + $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) diff --git a/tests/tcg/riscv64/issue1060.S b/tests/tcg/riscv64/issue1060.S new file mode 100644 index 00000000000..17b7fe1be22 --- /dev/null +++ b/tests/tcg/riscv64/issue1060.S @@ -0,0 +1,53 @@ + .option norvc + + .text + .global _start +_start: + lla t0, trap + csrw mtvec, t0 + + # These are all illegal instructions + csrw time, x0 + .insn i CUSTOM_0, 0, x0, x0, 0x321 + csrw time, x0 + .insn i CUSTOM_0, 0, x0, x0, 0x123 + csrw cycle, x0 + + # Success! + li a0, 0 + j _exit + +trap: + # When an instruction traps, compare it to the insn in memory. + csrr t0, mepc + csrr t1, mtval + lwu t2, 0(t0) + bne t1, t2, fail + + # Skip the insn and continue. + addi t0, t0, 4 + csrw mepc, t0 + mret + +fail: + li a0, 1 + +# Exit code in a0 +_exit: + lla a1, semiargs + li t0, 0x20026 # ADP_Stopped_ApplicationExit + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED + + # Semihosting call sequence + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 diff --git a/tests/tcg/riscv64/semihost.ld b/tests/tcg/riscv64/semihost.ld new file mode 100644 index 00000000000..a59cc56b289 --- /dev/null +++ b/tests/tcg/riscv64/semihost.ld @@ -0,0 +1,21 @@ +ENTRY(_start) + +SECTIONS +{ + /* virt machine, RAM starts at 2gb */ + . = 0x80000000; + .text : { + *(.text) + } + .rodata : { + *(.rodata) + } + /* align r/w section to next 2mb */ + . = ALIGN(1 << 21); + .data : { + *(.data) + } + .bss : { + *(.bss) + } +} diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 31241727361..1a7a4a2f593 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -17,6 +17,13 @@ TESTS+=trap TESTS+=signals-s390x TESTS+=branch-relative-long +Z14_TESTS=vfminmax +vfminmax: LDFLAGS+=-lm +$(Z14_TESTS): CFLAGS+=-march=z14 -O2 + +TESTS+=$(if $(shell $(CC) -march=z14 -S -o /dev/null -xc /dev/null \ + >/dev/null 2>&1 && echo OK),$(Z14_TESTS)) + VECTOR_TESTS=vxeh2_vs VECTOR_TESTS+=vxeh2_vcvt VECTOR_TESTS+=vxeh2_vlstr diff --git a/tests/tcg/s390x/vfminmax.c b/tests/tcg/s390x/vfminmax.c new file mode 100644 index 00000000000..22629df160e --- /dev/null +++ b/tests/tcg/s390x/vfminmax.c @@ -0,0 +1,411 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +/* + * vfmin/vfmax instruction execution. + */ +#define VFMIN 0xEE +#define VFMAX 0xEF + +extern char insn[6]; +asm(".pushsection .rwx,\"awx\",@progbits\n" + ".globl insn\n" + /* e7 89 a0 00 2e ef */ + "insn: vfmaxsb %v24,%v25,%v26,0\n" + ".popsection\n"); + +static void vfminmax(unsigned int op, + unsigned int m4, unsigned int m5, unsigned int m6, + void *v1, const void *v2, const void *v3) +{ + insn[3] = (m6 << 4) | m5; + insn[4] = (m4 << 4) | 0x0e; + insn[5] = op; + + asm("vl %%v25,%[v2]\n" + "vl %%v26,%[v3]\n" + "ex 0,%[insn]\n" + "vst %%v24,%[v1]\n" + : [v1] "=m" (*(char (*)[16])v1) + : [v2] "m" (*(char (*)[16])v2) + , [v3] "m" (*(char (*)[16])v3) + , [insn] "m"(insn) + : "v24", "v25", "v26"); +} + +/* + * Floating-point value classes. + */ +#define N_FORMATS 3 +#define N_SIGNED_CLASSES 8 +static const size_t float_sizes[N_FORMATS] = { + /* M4 == 2: short */ 4, + /* M4 == 3: long */ 8, + /* M4 == 4: extended */ 16, +}; +static const size_t e_bits[N_FORMATS] = { + /* M4 == 2: short */ 8, + /* M4 == 3: long */ 11, + /* M4 == 4: extended */ 15, +}; +static const unsigned char signed_floats[N_FORMATS][N_SIGNED_CLASSES][2][16] = { + /* M4 == 2: short */ + { + /* -inf */ {{0xff, 0x80, 0x00, 0x00}, + {0xff, 0x80, 0x00, 0x00}}, + /* -Fn */ {{0xc2, 0x28, 0x00, 0x00}, + {0xc2, 0x29, 0x00, 0x00}}, + /* -0 */ {{0x80, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}}, + /* +0 */ {{0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}}, + /* +Fn */ {{0x42, 0x28, 0x00, 0x00}, + {0x42, 0x2a, 0x00, 0x00}}, + /* +inf */ {{0x7f, 0x80, 0x00, 0x00}, + {0x7f, 0x80, 0x00, 0x00}}, + /* QNaN */ {{0x7f, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xfe}}, + /* SNaN */ {{0x7f, 0xbf, 0xff, 0xff}, + {0x7f, 0xbf, 0xff, 0xfd}}, + }, + + /* M4 == 3: long */ + { + /* -inf */ {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -Fn */ {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +Fn */ {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +inf */ {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, + /* SNaN */ {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, + }, + + /* M4 == 4: extended */ + { + /* -inf */ {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -Fn */ {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +Fn */ {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +inf */ {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, + /* SNaN */ {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, + }, +}; + +/* + * PoP tables as close to the original as possible. + */ +struct signed_test { + int op; + int m6; + const char *m6_desc; + const char *table[N_SIGNED_CLASSES][N_SIGNED_CLASSES]; +} signed_tests[] = { + { + .op = VFMIN, + .m6 = 0, + .m6_desc = "IEEE MinNum", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMIN, + .m6 = 1, + .m6_desc = "JAVA Math.Min()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* -0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* QNaN */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMIN, + .m6 = 2, + .m6_desc = "C-style Min Macro", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* -0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* QNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + }, + }, + { + .op = VFMIN, + .m6 = 3, + .m6_desc = "C++ algorithm.min()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* +0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* QNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, + { + .op = VFMIN, + .m6 = 4, + .m6_desc = "fmin()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(a)", "Xi: T(a)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, + + { + .op = VFMAX, + .m6 = 0, + .m6_desc = "IEEE MaxNum", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMAX, + .m6 = 1, + .m6_desc = "JAVA Math.Max()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* QNaN */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMAX, + .m6 = 2, + .m6_desc = "C-style Max Macro", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* -0 */ "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* +0 */ "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* QNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + }, + }, + { + .op = VFMAX, + .m6 = 3, + .m6_desc = "C++ algorithm.max()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* QNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, + { + .op = VFMAX, + .m6 = 4, + .m6_desc = "fmax()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(a)", "Xi: T(a)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, +}; + +static void dump_v(FILE *f, const void *v, size_t n) +{ + for (int i = 0; i < n; i++) { + fprintf(f, "%02x", ((const unsigned char *)v)[i]); + } +} + +static int signed_test(struct signed_test *test, int m4, int m5, + const void *v1_exp, bool xi_exp, + const void *v2, const void *v3) +{ + size_t n = (m5 & 8) ? float_sizes[m4 - 2] : 16; + char v1[16]; + bool xi; + + feclearexcept(FE_ALL_EXCEPT); + vfminmax(test->op, m4, m5, test->m6, v1, v2, v3); + xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID; + + if (memcmp(v1, v1_exp, n) != 0 || xi != xi_exp) { + fprintf(stderr, "[ FAILED ] %s ", test->m6_desc); + dump_v(stderr, v2, n); + fprintf(stderr, ", "); + dump_v(stderr, v3, n); + fprintf(stderr, ", %d, %d, %d: actual=", m4, m5, test->m6); + dump_v(stderr, v1, n); + fprintf(stderr, "/%d, expected=", (int)xi); + dump_v(stderr, v1_exp, n); + fprintf(stderr, "/%d\n", (int)xi_exp); + return 1; + } + + return 0; +} + +static void snan_to_qnan(char *v, int m4) +{ + size_t bit = 1 + e_bits[m4 - 2]; + v[bit / 8] |= 1 << (7 - (bit % 8)); +} + +int main(void) +{ + int ret = 0; + size_t i; + + for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) { + struct signed_test *test = &signed_tests[i]; + int m4; + + for (m4 = 2; m4 <= 4; m4++) { + const unsigned char (*floats)[2][16] = signed_floats[m4 - 2]; + size_t float_size = float_sizes[m4 - 2]; + int m5; + + for (m5 = 0; m5 <= 8; m5 += 8) { + char v1_exp[16], v2[16], v3[16]; + bool xi_exp = false; + int pos = 0; + int i2; + + for (i2 = 0; i2 < N_SIGNED_CLASSES * 2; i2++) { + int i3; + + for (i3 = 0; i3 < N_SIGNED_CLASSES * 2; i3++) { + const char *spec = test->table[i2 / 2][i3 / 2]; + + memcpy(&v2[pos], floats[i2 / 2][i2 % 2], float_size); + memcpy(&v3[pos], floats[i3 / 2][i3 % 2], float_size); + if (strcmp(spec, "T(a)") == 0 || + strcmp(spec, "Xi: T(a)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + } else if (strcmp(spec, "T(b)") == 0 || + strcmp(spec, "Xi: T(b)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + } else if (strcmp(spec, "Xi: T(a*)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + snan_to_qnan(&v1_exp[pos], m4); + } else if (strcmp(spec, "Xi: T(b*)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + snan_to_qnan(&v1_exp[pos], m4); + } else if (strcmp(spec, "T(M(a,b))") == 0) { + /* + * Comparing floats is risky, since the compiler + * might generate the same instruction that we are + * testing. Compare ints instead. This works, + * because we get here only for +-Fn, and the + * corresponding test values have identical + * exponents. + */ + int v2_int = *(int *)&v2[pos]; + int v3_int = *(int *)&v3[pos]; + + if ((v2_int < v3_int) == + ((test->op == VFMIN) != (v2_int < 0))) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + } else { + memcpy(&v1_exp[pos], &v3[pos], float_size); + } + } else { + fprintf(stderr, "Unexpected spec: %s\n", spec); + return 1; + } + xi_exp |= spec[0] == 'X'; + pos += float_size; + + if ((m5 & 8) || pos == 16) { + ret |= signed_test(test, m4, m5, + v1_exp, xi_exp, v2, v3); + pos = 0; + xi_exp = false; + } + } + } + + if (pos != 0) { + ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3); + } + } + } + } + + return ret; +} diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 287b367ec30..b497a413783 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -88,6 +88,7 @@ if have_block 'test-io-channel-file': ['io-channel-helpers.c', io], 'test-io-channel-command': ['io-channel-helpers.c', io], 'test-io-channel-buffer': ['io-channel-helpers.c', io], + 'test-io-channel-null': [io], 'test-crypto-ivgen': [io], 'test-crypto-afsplit': [io], 'test-crypto-block': [io], diff --git a/tests/unit/ptimer-test.c b/tests/unit/ptimer-test.c index a80ef5aff4c..04b5f4e3d03 100644 --- a/tests/unit/ptimer-test.c +++ b/tests/unit/ptimer-test.c @@ -798,64 +798,64 @@ static void add_ptimer_tests(uint8_t policy) g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name), - g_memdup(&policy, 1), check_set_count, g_free); + g_memdup2(&policy, 1), check_set_count, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/set_limit policy=%s", policy_name), - g_memdup(&policy, 1), check_set_limit, g_free); + g_memdup2(&policy, 1), check_set_limit, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/oneshot policy=%s", policy_name), - g_memdup(&policy, 1), check_oneshot, g_free); + g_memdup2(&policy, 1), check_oneshot, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/periodic policy=%s", policy_name), - g_memdup(&policy, 1), check_periodic, g_free); + g_memdup2(&policy, 1), check_periodic, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/on_the_fly_mode_change policy=%s", policy_name), - g_memdup(&policy, 1), check_on_the_fly_mode_change, g_free); + g_memdup2(&policy, 1), check_on_the_fly_mode_change, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/on_the_fly_period_change policy=%s", policy_name), - g_memdup(&policy, 1), check_on_the_fly_period_change, g_free); + g_memdup2(&policy, 1), check_on_the_fly_period_change, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/on_the_fly_freq_change policy=%s", policy_name), - g_memdup(&policy, 1), check_on_the_fly_freq_change, g_free); + g_memdup2(&policy, 1), check_on_the_fly_freq_change, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/run_with_period_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_run_with_period_0, g_free); + g_memdup2(&policy, 1), check_run_with_period_0, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/run_with_delta_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_run_with_delta_0, g_free); + g_memdup2(&policy, 1), check_run_with_delta_0, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/periodic_with_load_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_periodic_with_load_0, g_free); + g_memdup2(&policy, 1), check_periodic_with_load_0, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/oneshot_with_load_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_oneshot_with_load_0, g_free); + g_memdup2(&policy, 1), check_oneshot_with_load_0, g_free); g_free(tmp); } diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 94718c9319b..8b55eccc899 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -88,11 +88,11 @@ static void test_sync_op_pread(BdrvChild *c) int ret; /* Success */ - ret = bdrv_pread(c, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); + ret = bdrv_pread(c, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = bdrv_pread(c, -2, buf, sizeof(buf)); + ret = bdrv_pread(c, -2, sizeof(buf), buf, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -102,11 +102,11 @@ static void test_sync_op_pwrite(BdrvChild *c) int ret; /* Success */ - ret = bdrv_pwrite(c, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); + ret = bdrv_pwrite(c, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = bdrv_pwrite(c, -2, buf, sizeof(buf)); + ret = bdrv_pwrite(c, -2, sizeof(buf), buf, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -116,11 +116,11 @@ static void test_sync_op_blk_pread(BlockBackend *blk) int ret; /* Success */ - ret = blk_pread(blk, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); + ret = blk_pread(blk, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = blk_pread(blk, -2, buf, sizeof(buf)); + ret = blk_pread(blk, -2, sizeof(buf), buf, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -130,11 +130,98 @@ static void test_sync_op_blk_pwrite(BlockBackend *blk) int ret; /* Success */ - ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0); - g_assert_cmpint(ret, ==, 512); + ret = blk_pwrite(blk, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwrite(blk, -2, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_preadv(BlockBackend *blk) +{ + uint8_t buf[512]; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_preadv(blk, 0, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0); + ret = blk_preadv(blk, -2, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwritev(BlockBackend *blk) +{ + uint8_t buf[512] = { 0 }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_pwritev(blk, 0, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwritev(blk, -2, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_preadv_part(BlockBackend *blk) +{ + uint8_t buf[512]; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_preadv_part(blk, 0, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_preadv_part(blk, -2, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwritev_part(BlockBackend *blk) +{ + uint8_t buf[512] = { 0 }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_pwritev_part(blk, 0, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwritev_part(blk, -2, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwrite_compressed(BlockBackend *blk) +{ + uint8_t buf[512] = { 0 }; + int ret; + + /* Late error: Not supported */ + ret = blk_pwrite_compressed(blk, 0, sizeof(buf), buf); + g_assert_cmpint(ret, ==, -ENOTSUP); + + /* Early error: Negative offset */ + ret = blk_pwrite_compressed(blk, -2, sizeof(buf), buf); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwrite_zeroes(BlockBackend *blk) +{ + int ret; + + /* Success */ + ret = blk_pwrite_zeroes(blk, 0, 512, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwrite_zeroes(blk, -2, 512, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -211,6 +298,19 @@ static void test_sync_op_truncate(BdrvChild *c) c->bs->open_flags |= BDRV_O_RDWR; } +static void test_sync_op_blk_truncate(BlockBackend *blk) +{ + int ret; + + /* Normal success path */ + ret = blk_truncate(blk, 65536, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_truncate(blk, -2, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, -EINVAL); +} + static void test_sync_op_block_status(BdrvChild *c) { int ret; @@ -301,6 +401,30 @@ const SyncOpTest sync_op_tests[] = { .name = "/sync-op/pwrite", .fn = test_sync_op_pwrite, .blkfn = test_sync_op_blk_pwrite, + }, { + .name = "/sync-op/preadv", + .fn = NULL, + .blkfn = test_sync_op_blk_preadv, + }, { + .name = "/sync-op/pwritev", + .fn = NULL, + .blkfn = test_sync_op_blk_pwritev, + }, { + .name = "/sync-op/preadv_part", + .fn = NULL, + .blkfn = test_sync_op_blk_preadv_part, + }, { + .name = "/sync-op/pwritev_part", + .fn = NULL, + .blkfn = test_sync_op_blk_pwritev_part, + }, { + .name = "/sync-op/pwrite_compressed", + .fn = NULL, + .blkfn = test_sync_op_blk_pwrite_compressed, + }, { + .name = "/sync-op/pwrite_zeroes", + .fn = NULL, + .blkfn = test_sync_op_blk_pwrite_zeroes, }, { .name = "/sync-op/load_vmstate", .fn = test_sync_op_load_vmstate, @@ -314,6 +438,7 @@ const SyncOpTest sync_op_tests[] = { }, { .name = "/sync-op/truncate", .fn = test_sync_op_truncate, + .blkfn = test_sync_op_blk_truncate, }, { .name = "/sync-op/block_status", .fn = test_sync_op_block_status, @@ -349,7 +474,9 @@ static void test_sync_op(const void *opaque) blk_set_aio_context(blk, ctx, &error_abort); aio_context_acquire(ctx); - t->fn(c); + if (t->fn) { + t->fn(c); + } if (t->blkfn) { t->blkfn(blk); } diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 3b1f0d509fa..3417b67be5d 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -188,12 +188,12 @@ static struct QCryptoBlockTestData { }; -static ssize_t test_block_read_func(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int test_block_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { Buffer *header = opaque; @@ -201,14 +201,14 @@ static ssize_t test_block_read_func(QCryptoBlock *block, memcpy(buf, header->buffer + offset, buflen); - return buflen; + return 0; } -static ssize_t test_block_init_func(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp) +static int test_block_init_func(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp) { Buffer *header = opaque; @@ -216,16 +216,16 @@ static ssize_t test_block_init_func(QCryptoBlock *block, buffer_reserve(header, headerlen); - return headerlen; + return 0; } -static ssize_t test_block_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int test_block_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { Buffer *header = opaque; @@ -234,7 +234,7 @@ static ssize_t test_block_write_func(QCryptoBlock *block, memcpy(header->buffer + offset, buf, buflen); header->offset = offset + buflen; - return buflen; + return 0; } diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 98671f1ac30..86caddcf649 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -2450,6 +2450,76 @@ static void test_qemu_strtosz_metric(void) g_assert(endptr == str + 7); } +static void test_freq_to_str(void) +{ + char *str; + + str = freq_to_str(999); + g_assert_cmpstr(str, ==, "999 Hz"); + g_free(str); + + str = freq_to_str(1000); + g_assert_cmpstr(str, ==, "1 KHz"); + g_free(str); + + str = freq_to_str(1010); + g_assert_cmpstr(str, ==, "1.01 KHz"); + g_free(str); +} + +static void test_size_to_str(void) +{ + char *str; + + str = size_to_str(0); + g_assert_cmpstr(str, ==, "0 B"); + g_free(str); + + str = size_to_str(1); + g_assert_cmpstr(str, ==, "1 B"); + g_free(str); + + str = size_to_str(1016); + g_assert_cmpstr(str, ==, "0.992 KiB"); + g_free(str); + + str = size_to_str(1024); + g_assert_cmpstr(str, ==, "1 KiB"); + g_free(str); + + str = size_to_str(512ull << 20); + g_assert_cmpstr(str, ==, "512 MiB"); + g_free(str); +} + +static void test_iec_binary_prefix(void) +{ + g_assert_cmpstr(iec_binary_prefix(0), ==, ""); + g_assert_cmpstr(iec_binary_prefix(10), ==, "Ki"); + g_assert_cmpstr(iec_binary_prefix(20), ==, "Mi"); + g_assert_cmpstr(iec_binary_prefix(30), ==, "Gi"); + g_assert_cmpstr(iec_binary_prefix(40), ==, "Ti"); + g_assert_cmpstr(iec_binary_prefix(50), ==, "Pi"); + g_assert_cmpstr(iec_binary_prefix(60), ==, "Ei"); +} + +static void test_si_prefix(void) +{ + g_assert_cmpstr(si_prefix(-18), ==, "a"); + g_assert_cmpstr(si_prefix(-15), ==, "f"); + g_assert_cmpstr(si_prefix(-12), ==, "p"); + g_assert_cmpstr(si_prefix(-9), ==, "n"); + g_assert_cmpstr(si_prefix(-6), ==, "u"); + g_assert_cmpstr(si_prefix(-3), ==, "m"); + g_assert_cmpstr(si_prefix(0), ==, ""); + g_assert_cmpstr(si_prefix(3), ==, "K"); + g_assert_cmpstr(si_prefix(6), ==, "M"); + g_assert_cmpstr(si_prefix(9), ==, "G"); + g_assert_cmpstr(si_prefix(12), ==, "T"); + g_assert_cmpstr(si_prefix(15), ==, "P"); + g_assert_cmpstr(si_prefix(18), ==, "E"); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -2729,5 +2799,13 @@ int main(int argc, char **argv) g_test_add_func("/cutils/strtosz/metric", test_qemu_strtosz_metric); + g_test_add_func("/cutils/size_to_str", + test_size_to_str); + g_test_add_func("/cutils/freq_to_str", + test_freq_to_str); + g_test_add_func("/cutils/iec_binary_prefix", + test_iec_binary_prefix); + g_test_add_func("/cutils/si_prefix", + test_si_prefix); return g_test_run(); } diff --git a/tests/unit/test-io-channel-null.c b/tests/unit/test-io-channel-null.c new file mode 100644 index 00000000000..b3aab17ccce --- /dev/null +++ b/tests/unit/test-io-channel-null.c @@ -0,0 +1,95 @@ +/* + * QEMU I/O channel null test + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-null.h" +#include "qapi/error.h" + +static gboolean test_io_channel_watch(QIOChannel *ioc, + GIOCondition condition, + gpointer opaque) +{ + GIOCondition *gotcond = opaque; + *gotcond = condition; + return G_SOURCE_REMOVE; +} + +static void test_io_channel_null_io(void) +{ + g_autoptr(QIOChannelNull) null = qio_channel_null_new(); + char buf[1024]; + GIOCondition gotcond = 0; + Error *local_err = NULL; + + g_assert(qio_channel_write(QIO_CHANNEL(null), + "Hello World", 11, + &error_abort) == 11); + + g_assert(qio_channel_read(QIO_CHANNEL(null), + buf, sizeof(buf), + &error_abort) == 0); + + qio_channel_add_watch(QIO_CHANNEL(null), + G_IO_IN, + test_io_channel_watch, + &gotcond, + NULL); + + g_main_context_iteration(NULL, false); + + g_assert(gotcond == G_IO_IN); + + qio_channel_add_watch(QIO_CHANNEL(null), + G_IO_IN | G_IO_OUT, + test_io_channel_watch, + &gotcond, + NULL); + + g_main_context_iteration(NULL, false); + + g_assert(gotcond == (G_IO_IN | G_IO_OUT)); + + qio_channel_close(QIO_CHANNEL(null), &error_abort); + + g_assert(qio_channel_write(QIO_CHANNEL(null), + "Hello World", 11, + &local_err) == -1); + g_assert_nonnull(local_err); + + g_clear_pointer(&local_err, error_free); + + g_assert(qio_channel_read(QIO_CHANNEL(null), + buf, sizeof(buf), + &local_err) == -1); + g_assert_nonnull(local_err); + + g_clear_pointer(&local_err, error_free); +} + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/null/io", test_io_channel_null_io); + + return g_test_run(); +} diff --git a/tests/unit/test-iov.c b/tests/unit/test-iov.c index 93bda00f0e1..6f7623d3107 100644 --- a/tests/unit/test-iov.c +++ b/tests/unit/test-iov.c @@ -172,7 +172,7 @@ static void test_io(void) } iov_from_buf(iov, niov, 0, buf, sz); - siov = g_memdup(iov, sizeof(*iov) * niov); + siov = g_memdup2(iov, sizeof(*iov) * niov); if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { perror("socketpair"); @@ -349,7 +349,7 @@ static void test_discard_front_undo(void) /* Discard zero bytes */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, 0, &undo); @@ -360,7 +360,7 @@ static void test_discard_front_undo(void) /* Discard more bytes than vector size */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); @@ -372,7 +372,7 @@ static void test_discard_front_undo(void) /* Discard entire vector */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); @@ -384,7 +384,7 @@ static void test_discard_front_undo(void) /* Discard within first element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = g_test_rand_int_range(1, iov->iov_len); @@ -396,7 +396,7 @@ static void test_discard_front_undo(void) /* Discard entire first element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, iov->iov_len, &undo); @@ -407,7 +407,7 @@ static void test_discard_front_undo(void) /* Discard within second element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); @@ -498,7 +498,7 @@ static void test_discard_back_undo(void) /* Discard zero bytes */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; iov_discard_back_undoable(iov, &iov_cnt_tmp, 0, &undo); iov_discard_undo(&undo); @@ -508,7 +508,7 @@ static void test_discard_back_undo(void) /* Discard more bytes than vector size */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); iov_discard_back_undoable(iov, &iov_cnt_tmp, size + 1, &undo); @@ -519,7 +519,7 @@ static void test_discard_back_undo(void) /* Discard entire vector */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); @@ -530,7 +530,7 @@ static void test_discard_back_undo(void) /* Discard within last element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); @@ -541,7 +541,7 @@ static void test_discard_back_undo(void) /* Discard entire last element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov[iov_cnt - 1].iov_len; iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); @@ -552,7 +552,7 @@ static void test_discard_back_undo(void) /* Discard within second-to-last element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov[iov_cnt - 1].iov_len + g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 6a417bb1029..72077b57800 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -28,7 +28,6 @@ #include "migration/vmstate.h" #include "migration/qemu-file-types.h" #include "../migration/qemu-file.h" -#include "../migration/qemu-file-channel.h" #include "../migration/savevm.h" #include "qemu/coroutine.h" #include "qemu/module.h" @@ -52,9 +51,9 @@ static QEMUFile *open_test_file(bool write) } ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); if (write) { - f = qemu_fopen_channel_output(ioc); + f = qemu_file_new_output(ioc); } else { - f = qemu_fopen_channel_input(ioc); + f = qemu_file_new_input(ioc); } object_unref(OBJECT(ioc)); return f; diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 588bc999cc9..8d2a164552d 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -1,14 +1,23 @@ # Makefile for VM tests -.PHONY: vm-build-all vm-clean-all +# Hack to allow running in an unconfigured build tree +ifeq ($(wildcard $(SRC_PATH)/config-host.mak),) +VM_PYTHON = PYTHONPATH=$(SRC_PATH)/python /usr/bin/env python3 +VM_VENV = +HOST_ARCH := $(shell uname -m) +else +VM_PYTHON = $(TESTS_PYTHON) +VM_VENV = check-venv +HOST_ARCH = $(ARCH) +endif -HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) +.PHONY: vm-build-all vm-clean-all EFI_AARCH64 = $(wildcard $(BUILD_DIR)/pc-bios/edk2-aarch64-code.fd) -X86_IMAGES := freebsd netbsd openbsd centos fedora haiku.x86_64 +X86_IMAGES := freebsd netbsd openbsd fedora haiku.x86_64 ifneq ($(GENISOIMAGE),) -X86_IMAGES += ubuntu.i386 centos +X86_IMAGES += centos ifneq ($(EFI_AARCH64),) ARM64_IMAGES += ubuntu.aarch64 centos.aarch64 endif @@ -39,7 +48,6 @@ vm-help vm-test: @echo " vm-build-fedora - Build QEMU in Fedora VM" ifneq ($(GENISOIMAGE),) @echo " vm-build-centos - Build QEMU in CentOS VM, with Docker" - @echo " vm-build-ubuntu.i386 - Build QEMU in ubuntu i386 VM" ifneq ($(EFI_AARCH64),) @echo " vm-build-ubuntu.aarch64 - Build QEMU in ubuntu aarch64 VM" @echo " vm-build-centos.aarch64 - Build QEMU in CentOS aarch64 VM" @@ -85,10 +93,10 @@ vm-clean-all: $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ $(SRC_PATH)/tests/vm/basevm.py \ $(SRC_PATH)/tests/vm/Makefile.include \ - check-venv + $(VM_VENV) @mkdir -p $(IMAGES_DIR) $(call quiet-command, \ - $(TESTS_PYTHON) $< \ + $(VM_PYTHON) $< \ $(if $(V)$(DEBUG), --debug) \ $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ @@ -100,11 +108,10 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ --build-image $@, \ " VM-IMAGE $*") - # Build in VM $(IMAGE) -vm-build-%: $(IMAGES_DIR)/%.img check-venv +vm-build-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(call quiet-command, \ - $(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ + $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(V)$(DEBUG), --debug) \ $(if $(DEBUG), --interactive) \ $(if $(J),--jobs $(J)) \ @@ -128,9 +135,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img -device virtio-net-pci,netdev=vnet \ || true -vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv +vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(call quiet-command, \ - $(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ + $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(J),--jobs $(J)) \ $(if $(V)$(DEBUG), --debug) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index d7d0413df35..4fd9af10b7f 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -99,6 +99,11 @@ def __init__(self, args, config=None): self._source_path = args.source_path # Allow input config to override defaults. self._config = DEFAULT_CONFIG.copy() + + # 1GB per core, minimum of 4. This is only a default. + mem = max(4, args.jobs) + self._config['memory'] = f"{mem}G" + if config != None: self._config.update(config) self.validate_ssh_keys() diff --git a/tests/vm/centos b/tests/vm/centos index 5c7bc1c1a9a..097a9ca14d3 100755 --- a/tests/vm/centos +++ b/tests/vm/centos @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # -# CentOS image +# CentOS 8 Stream image # -# Copyright 2018 Red Hat Inc. +# Copyright 2018, 2022 Red Hat Inc. # # Authors: # Fam Zheng @@ -28,13 +28,12 @@ class CentosVM(basevm.BaseVM): tar -xf $SRC_ARCHIVE; make docker-test-block@centos8 {verbose} J={jobs} NETWORK=1; make docker-test-quick@centos8 {verbose} J={jobs} NETWORK=1; - make docker-test-mingw@fedora {verbose} J={jobs} NETWORK=1; """ def build_image(self, img): - cimg = self._download_with_cache("https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.3.2011-20201204.2.x86_64.qcow2") + cimg = self._download_with_cache("https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20220125.1.x86_64.qcow2") img_tmp = img + ".tmp" - subprocess.check_call(["ln", "-f", cimg, img_tmp]) + subprocess.check_call(['cp', '-f', cimg, img_tmp]) self.exec_qemu_img("resize", img_tmp, "50G") self.boot(img_tmp, extra_args = ["-cdrom", self.gen_cloud_init_iso()]) self.wait_ssh() diff --git a/tests/vm/centos.aarch64 b/tests/vm/centos.aarch64 index 96c450f8be5..2de7ef6992c 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -20,150 +20,38 @@ import time import traceback import aarch64vm + DEFAULT_CONFIG = { 'cpu' : "max", 'machine' : "virt,gic-version=max", - 'install_cmds' : "yum install -y make ninja-build git python3 gcc gcc-c++ flex bison, "\ - "yum install -y glib2-devel perl pixman-devel zlib-devel, "\ - "alternatives --set python /usr/bin/python3, "\ - "sudo dnf config-manager "\ - "--add-repo=https://download.docker.com/linux/centos/docker-ce.repo,"\ - "sudo dnf install --nobest -y docker-ce.aarch64,"\ - "systemctl enable docker", + 'install_cmds' : ( + "dnf config-manager --set-enabled powertools, " + "dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo, " + "dnf install -y make ninja-build git python38 gcc gcc-c++ flex bison "\ + "glib2-devel perl pixman-devel zlib-devel docker-ce.aarch64, " + "systemctl enable docker, " + ), # We increase beyond the default time since during boot # it can take some time (many seconds) to log into the VM. 'ssh_timeout' : 60, } + class CentosAarch64VM(basevm.BaseVM): - name = "centos.aarch64" + name = "centos8.aarch64" arch = "aarch64" - login_prompt = "localhost login:" - prompt = '[root@localhost ~]#' - image_name = "CentOS-8-aarch64-1905-dvd1.iso" - image_link = "http://mirrors.usc.edu/pub/linux/distributions/centos/8.0.1905/isos/aarch64/" + image_name = "CentOS-Stream-GenericCloud-8-20220125.1.aarch64.qcow2" + image_link = "https://cloud.centos.org/centos/8-stream/aarch64/images/" image_link += image_name BUILD_SCRIPT = """ set -e; cd $(mktemp -d); - sudo chmod a+r /dev/vdb; - tar --checkpoint=.10 -xf /dev/vdb; + export SRC_ARCHIVE=/dev/vdb; + sudo chmod a+r $SRC_ARCHIVE; + tar -xf $SRC_ARCHIVE; ./configure {configure_opts}; make --output-sync {target} -j{jobs} {verbose}; """ - def set_key_perm(self): - """Set permissions properly on certain files to allow - ssh access.""" - self.console_wait_send(self.prompt, - "/usr/sbin/restorecon -R -v /root/.ssh\n") - self.console_wait_send(self.prompt, - "/usr/sbin/restorecon -R -v "\ - "/home/{}/.ssh\n".format(self._config["guest_user"])) - - def create_kickstart(self): - """Generate the kickstart file used to generate the centos image.""" - # Start with the template for the kickstart. - ks_file = self._source_path + "/tests/vm/centos-8-aarch64.ks" - subprocess.check_call("cp {} ./ks.cfg".format(ks_file), shell=True) - # Append the ssh keys to the kickstart file - # as the post processing phase of installation. - with open("ks.cfg", "a") as f: - # Add in the root pw and guest user. - rootpw = "rootpw --plaintext {}\n" - f.write(rootpw.format(self._config["root_pass"])) - add_user = "user --groups=wheel --name={} "\ - "--password={} --plaintext\n" - f.write(add_user.format(self._config["guest_user"], - self._config["guest_pass"])) - # Add the ssh keys. - f.write("%post --log=/root/ks-post.log\n") - f.write("mkdir -p /root/.ssh\n") - addkey = 'echo "{}" >> /root/.ssh/authorized_keys\n' - addkey_cmd = addkey.format(self._config["ssh_pub_key"]) - f.write(addkey_cmd) - f.write('mkdir -p /home/{}/.ssh\n'.format(self._config["guest_user"])) - addkey = 'echo "{}" >> /home/{}/.ssh/authorized_keys\n' - addkey_cmd = addkey.format(self._config["ssh_pub_key"], - self._config["guest_user"]) - f.write(addkey_cmd) - f.write("%end\n") - # Take our kickstart file and create an .iso from it. - # The .iso will be provided to qemu as we boot - # from the install dvd. - # Anaconda will recognize the label "OEMDRV" and will - # start the automated installation. - gen_iso_img = 'genisoimage -output ks.iso -volid "OEMDRV" ks.cfg' - subprocess.check_call(gen_iso_img, shell=True) - - def wait_for_shutdown(self): - """We wait for qemu to shutdown the VM and exit. - While this happens we display the console view - for easier debugging.""" - # The image creation is essentially done, - # so whether or not the wait is successful we want to - # wait for qemu to exit (the self.wait()) before we return. - try: - self.console_wait("reboot: Power down") - except Exception as e: - sys.stderr.write("Exception hit\n") - if isinstance(e, SystemExit) and e.code == 0: - return 0 - traceback.print_exc() - finally: - self.wait() - - def build_base_image(self, dest_img): - """Run through the centos installer to create - a base image with name dest_img.""" - # We create the temp image, and only rename - # to destination when we are done. - img = dest_img + ".tmp" - # Create an empty image. - # We will provide this as the install destination. - qemu_img_create = "qemu-img create {} 50G".format(img) - subprocess.check_call(qemu_img_create, shell=True) - - # Create our kickstart file to be fed to the installer. - self.create_kickstart() - # Boot the install dvd with the params as our ks.iso - os_img = self._download_with_cache(self.image_link) - dvd_iso = "centos-8-dvd.iso" - subprocess.check_call(["cp", "-f", os_img, dvd_iso]) - extra_args = "-cdrom ks.iso" - extra_args += " -drive file={},if=none,id=drive1,cache=writeback" - extra_args += " -device virtio-blk,drive=drive1,bootindex=1" - extra_args = extra_args.format(dvd_iso).split(" ") - self.boot(img, extra_args=extra_args) - self.console_wait_send("change the selection", "\n") - # We seem to need to hit esc (chr(27)) twice to abort the - # media check, which takes a long time. - # Waiting a bit seems to be more reliable before hitting esc. - self.console_wait("Checking") - time.sleep(5) - self.console_wait_send("Checking", chr(27)) - time.sleep(5) - self.console_wait_send("Checking", chr(27)) - print("Found Checking") - # Give sufficient time for the installer to create the image. - self.console_init(timeout=7200) - self.wait_for_shutdown() - os.rename(img, dest_img) - print("Done with base image build: {}".format(dest_img)) - - def check_create_base_img(self, img_base, img_dest): - """Create a base image using the installer. - We will use the base image if it exists. - This helps cut down on install time in case we - need to restart image creation, - since the base image creation can take a long time.""" - if not os.path.exists(img_base): - print("Generate new base image: {}".format(img_base)) - self.build_base_image(img_base); - else: - print("Use existing base image: {}".format(img_base)) - # Save a copy of the base image and copy it to dest. - # which we will use going forward. - subprocess.check_call(["cp", img_base, img_dest]) def boot(self, img, extra_args=None): aarch64vm.create_flash_images(self._tmpdir, self._efi_aarch64) @@ -185,42 +73,28 @@ class CentosAarch64VM(basevm.BaseVM): super(CentosAarch64VM, self).boot(img, extra_args=extra_args) def build_image(self, img): + cimg = self._download_with_cache(self.image_link) img_tmp = img + ".tmp" - self.check_create_base_img(img + ".base", img_tmp) - - # Boot the new image for the first time to finish installation. - self.boot(img_tmp) - self.console_init() - self.console_wait_send(self.login_prompt, "root\n") - self.console_wait_send("Password:", - "{}\n".format(self._config["root_pass"])) - - self.set_key_perm() - self.console_wait_send(self.prompt, "rpm -q centos-release\n") - enable_adapter = "sed -i 's/ONBOOT=no/ONBOOT=yes/g'" \ - " /etc/sysconfig/network-scripts/ifcfg-enp0s1\n" - self.console_wait_send(self.prompt, enable_adapter) - self.console_wait_send(self.prompt, "ifup enp0s1\n") - self.console_wait_send(self.prompt, - 'echo "qemu ALL=(ALL) NOPASSWD:ALL" | '\ - 'sudo tee /etc/sudoers.d/qemu\n') - self.console_wait(self.prompt) - - # Rest of the commands we issue through ssh. + subprocess.run(['cp', '-f', cimg, img_tmp]) + self.exec_qemu_img("resize", img_tmp, "50G") + self.boot(img_tmp, extra_args = ["-cdrom", self.gen_cloud_init_iso()]) self.wait_ssh(wait_root=True) + self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") # If the user chooses *not* to do the second phase, # then we will jump right to the graceful shutdown if self._config['install_cmds'] != "": install_cmds = self._config['install_cmds'].split(',') for cmd in install_cmds: - self.ssh_root(cmd) + self.ssh_root_check(cmd) + self.ssh_root("poweroff") - self.wait_for_shutdown() + self.wait() os.rename(img_tmp, img) print("image creation complete: {}".format(img)) return 0 + if __name__ == "__main__": defaults = aarch64vm.get_config_defaults(CentosAarch64VM, DEFAULT_CONFIG) sys.exit(basevm.main(CentosAarch64VM, defaults)) diff --git a/tests/vm/fedora b/tests/vm/fedora index 92b78d6e2c9..12eca919a08 100755 --- a/tests/vm/fedora +++ b/tests/vm/fedora @@ -79,7 +79,6 @@ class FedoraVM(basevm.BaseVM): self.exec_qemu_img("create", "-f", "qcow2", img_tmp, self.size) self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", "-device", "VGA", "-cdrom", iso diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 805db759d67..cd1fabde523 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -95,7 +95,6 @@ class FreeBSDVM(basevm.BaseVM): self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", "-device", "VGA", "-cdrom", iso diff --git a/tests/vm/netbsd b/tests/vm/netbsd index 45aa9a7fda7..aa883ec23c9 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -86,7 +86,6 @@ class NetBSDVM(basevm.BaseVM): self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", "-cdrom", iso ]) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 13c82542140..6f1b6f5b98a 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -82,7 +82,6 @@ class OpenBSDVM(basevm.BaseVM): self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", "-device", "VGA", "-cdrom", iso diff --git a/tests/vm/ubuntu.aarch64 b/tests/vm/ubuntu.aarch64 index b291945a7e9..666947393bd 100755 --- a/tests/vm/ubuntu.aarch64 +++ b/tests/vm/ubuntu.aarch64 @@ -32,9 +32,13 @@ DEFAULT_CONFIG = { class UbuntuAarch64VM(ubuntuvm.UbuntuVM): name = "ubuntu.aarch64" arch = "aarch64" - image_name = "ubuntu-18.04-server-cloudimg-arm64.img" - image_link = "https://cloud-images.ubuntu.com/releases/18.04/release/" + image_name - image_sha256="0fdcba761965735a8a903d8b88df8e47f156f48715c00508e4315c506d7d3cb1" + # NOTE: The Ubuntu 20.04 cloud images are periodically updated. The + # fixed image chosen below is the latest release at time of + # writing. Using a rolling latest instead would mean that the SHA + # would be incorrect at an indeterminate point in the future. + image_name = "focal-server-cloudimg-arm64.img" + image_link = "https://cloud-images.ubuntu.com/focal/20220615/" + image_name + image_sha256="95a027336e197debe88c92ff2e554598e23c409139e1e750b71b3b820b514832" BUILD_SCRIPT = """ set -e; cd $(mktemp -d); diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386 deleted file mode 100755 index 47681b6f87d..00000000000 --- a/tests/vm/ubuntu.i386 +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -# -# Ubuntu i386 image -# -# Copyright 2017 Red Hat Inc. -# -# Authors: -# Fam Zheng -# -# This code is licensed under the GPL version 2 or later. See -# the COPYING file in the top-level directory. -# - -import sys -import basevm -import ubuntuvm - -DEFAULT_CONFIG = { - 'install_cmds' : "apt-get update,"\ - "apt-get build-dep -y qemu,"\ - "apt-get install -y libfdt-dev language-pack-en ninja-build", -} - -class UbuntuX86VM(ubuntuvm.UbuntuVM): - name = "ubuntu.i386" - arch = "i386" - image_link="https://cloud-images.ubuntu.com/releases/bionic/"\ - "release-20191114/ubuntu-18.04-server-cloudimg-i386.img" - image_sha256="28969840626d1ea80bb249c08eef1a4533e8904aa51a327b40f37ac4b4ff04ef" - BUILD_SCRIPT = """ - set -e; - cd $(mktemp -d); - sudo chmod a+r /dev/vdb; - tar -xf /dev/vdb; - ./configure {configure_opts}; - make --output-sync {target} -j{jobs} {verbose}; - """ - -if __name__ == "__main__": - sys.exit(basevm.main(UbuntuX86VM, DEFAULT_CONFIG)) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index b15c631ca51..7a73dfcce90 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -2319,7 +2319,7 @@ static int do_lo_create(fuse_req_t req, struct lo_inode *parent_inode, * If security.selinux has not been remapped and selinux is enabled, * use fscreate to set context before file creation. If not, use * tmpfile method for regular files. Otherwise fallback to - * non-atomic method of file creation and xattr settting. + * non-atomic method of file creation and xattr setting. */ if (!mapped_name && lo->use_fscreate) { err = do_create_secctx_fscreate(req, parent_inode, name, mode, fi, diff --git a/ui/cocoa.m b/ui/cocoa.m index 84c84e98fc5..5a8bd5dd84e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -561,8 +561,20 @@ - (void) updateUIInfoLocked CGDirectDisplayID display = [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]; NSSize screenSize = [[[self window] screen] frame].size; CGSize screenPhysicalSize = CGDisplayScreenSize(display); + CVDisplayLinkRef displayLink; frameSize = isFullscreen ? screenSize : [self frame].size; + + if (!CVDisplayLinkCreateWithCGDisplay(display, &displayLink)) { + CVTime period = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink); + CVDisplayLinkRelease(displayLink); + if (!(period.flags & kCVTimeIsIndefinite)) { + update_displaychangelistener(&dcl, + 1000 * period.timeValue / period.timeScale); + info.refresh_rate = (int64_t)1000 * period.timeScale / period.timeValue; + } + } + info.width_mm = frameSize.width / screenSize.width * screenPhysicalSize.width; info.height_mm = frameSize.height / screenSize.height * screenPhysicalSize.height; } else { @@ -800,7 +812,6 @@ - (bool) handleEventLocked:(NSEvent *)event int buttons = 0; int keycode = 0; bool mouse_event = false; - static bool switched_to_fullscreen = false; // Location of event in virtual screen coordinates NSPoint p = [self screenLocationOfEvent:event]; NSUInteger modifiers = [event modifierFlags]; @@ -952,13 +963,6 @@ - (bool) handleEventLocked:(NSEvent *)event // forward command key combos to the host UI unless the mouse is grabbed if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { - /* - * Prevent the command key from being stuck down in the guest - * when using Command-F to switch to full screen mode. - */ - if (keycode == Q_KEY_CODE_F) { - switched_to_fullscreen = true; - } return false; } @@ -1894,16 +1898,18 @@ static void cocoa_clipboard_notify(Notifier *notifier, void *data) static void cocoa_clipboard_request(QemuClipboardInfo *info, QemuClipboardType type) { + NSAutoreleasePool *pool; NSData *text; switch (type) { case QEMU_CLIPBOARD_TYPE_TEXT: + pool = [[NSAutoreleasePool alloc] init]; text = [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeString]; if (text) { qemu_clipboard_set_data(&cbpeer, info, type, [text length], [text bytes], true); - [text release]; } + [pool release]; break; default: break; diff --git a/ui/console.c b/ui/console.c index 9331b85203a..e139f7115e1 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2313,11 +2313,50 @@ bool qemu_console_is_gl_blocked(QemuConsole *con) return con->gl_block; } +bool qemu_console_is_multihead(DeviceState *dev) +{ + 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) { + continue; + } + + h = object_property_get_uint(OBJECT(con), + "head", &error_abort); + if (f == 0xffffffff) { + f = h; + } else if (h != f) { + return true; + } + } + return false; +} + char *qemu_console_get_label(QemuConsole *con) { if (con->console_type == GRAPHIC_CONSOLE) { if (con->device) { - return g_strdup(object_get_typename(con->device)); + DeviceState *dev; + bool multihead; + + dev = DEVICE(con->device); + multihead = qemu_console_is_multihead(dev); + if (multihead) { + return g_strdup_printf("%s.%d", dev->id ? + dev->id : + object_get_typename(con->device), + con->head); + } else { + return g_strdup_printf("%s", dev->id ? + dev->id : + object_get_typename(con->device)); + } } return g_strdup("VGA"); } else { diff --git a/ui/dbus.c b/ui/dbus.c index 7a87612379e..32d88dc94a7 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -268,6 +268,7 @@ dbus_display_add_client_ready(GObject *source_object, } g_dbus_object_manager_server_set_connection(dbus_display->server, conn); + g_dbus_connection_start_message_processing(conn); } @@ -300,7 +301,8 @@ dbus_display_add_client(int csock, Error **errp) g_dbus_connection_new(G_IO_STREAM(conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | + G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, NULL, dbus_display->add_client_cancellable, dbus_display_add_client_ready, diff --git a/ui/gtk.c b/ui/gtk.c index 2a791dd2aa0..1467b8c7d7f 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2390,6 +2390,10 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) opts->u.gtk.grab_on_hover) { gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); } + if (opts->u.gtk.has_show_tabs && + opts->u.gtk.show_tabs) { + gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item)); + } gd_clipboard_init(s); } diff --git a/ui/input.c b/ui/input.c index 8ac407dec48..e2a90af889e 100644 --- a/ui/input.c +++ b/ui/input.c @@ -364,7 +364,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) * when 'alt+print' was pressed. This flaw is now fixed and the * 'sysrq' key serves no further purpose. We normalize it to * 'print', so that downstream receivers of the event don't - * neeed to deal with this mistake + * need to deal with this mistake */ if (evt->type == INPUT_EVENT_KIND_KEY && evt->u.key.data->key->u.qcode.data == Q_KEY_CODE_SYSRQ) { diff --git a/util/aio-wait.c b/util/aio-wait.c index bdb3d3af22d..98c5accd29d 100644 --- a/util/aio-wait.c +++ b/util/aio-wait.c @@ -35,7 +35,21 @@ static void dummy_bh_cb(void *opaque) void aio_wait_kick(void) { - /* The barrier (or an atomic op) is in the caller. */ + /* + * Paired with smp_mb in AIO_WAIT_WHILE. Here we have: + * write(condition); + * aio_wait_kick() { + * smp_mb(); + * read(num_waiters); + * } + * + * And in AIO_WAIT_WHILE: + * write(num_waiters); + * smp_mb(); + * read(condition); + */ + smp_mb(); + if (qatomic_read(&global_aio_wait.num_waiters)) { aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); } diff --git a/util/cacheflush.c b/util/cacheflush.c index 4b57186d89c..2c2c73e085c 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -1,5 +1,5 @@ /* - * Flush the host cpu caches. + * Info about, and flushing the host cpu caches. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -9,8 +9,218 @@ #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" #include "qemu/bitops.h" +#include "qemu/host-utils.h" +#include "qemu/atomic.h" +int qemu_icache_linesize = 0; +int qemu_icache_linesize_log; +int qemu_dcache_linesize = 0; +int qemu_dcache_linesize_log; + +/* + * Operating system specific cache detection mechanisms. + */ + +#if defined(_WIN32) + +static void sys_cache_info(int *isize, int *dsize) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; + DWORD size = 0; + BOOL success; + size_t i, n; + + /* + * Check for the required buffer size first. Note that if the zero + * size we use for the probe results in success, then there is no + * data available; fail in that case. + */ + success = GetLogicalProcessorInformation(0, &size); + if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return; + } + + n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); + if (!GetLogicalProcessorInformation(buf, &size)) { + goto fail; + } + + for (i = 0; i < n; i++) { + if (buf[i].Relationship == RelationCache + && buf[i].Cache.Level == 1) { + switch (buf[i].Cache.Type) { + case CacheUnified: + *isize = *dsize = buf[i].Cache.LineSize; + break; + case CacheInstruction: + *isize = buf[i].Cache.LineSize; + break; + case CacheData: + *dsize = buf[i].Cache.LineSize; + break; + default: + break; + } + } + } + fail: + g_free(buf); +} + +#elif defined(CONFIG_DARWIN) +# include +static void sys_cache_info(int *isize, int *dsize) +{ + /* There's only a single sysctl for both I/D cache line sizes. */ + long size; + size_t len = sizeof(size); + if (!sysctlbyname("hw.cachelinesize", &size, &len, NULL, 0)) { + *isize = *dsize = size; + } +} +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +# include +static void sys_cache_info(int *isize, int *dsize) +{ + /* There's only a single sysctl for both I/D cache line sizes. */ + int size; + size_t len = sizeof(size); + if (!sysctlbyname("machdep.cacheline_size", &size, &len, NULL, 0)) { + *isize = *dsize = size; + } +} +#else +/* POSIX */ + +static void sys_cache_info(int *isize, int *dsize) +{ +# ifdef _SC_LEVEL1_ICACHE_LINESIZE + int tmp_isize = (int) sysconf(_SC_LEVEL1_ICACHE_LINESIZE); + if (tmp_isize > 0) { + *isize = tmp_isize; + } +# endif +# ifdef _SC_LEVEL1_DCACHE_LINESIZE + int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + if (tmp_dsize > 0) { + *dsize = tmp_dsize; + } +# endif +} +#endif /* sys_cache_info */ + + +/* + * Architecture (+ OS) specific cache detection mechanisms. + */ + +#if defined(__powerpc__) +static bool have_coherent_icache; +#endif + +#if defined(__aarch64__) && !defined(CONFIG_DARWIN) +/* Apple does not expose CTR_EL0, so we must use system interfaces. */ +static uint64_t save_ctr_el0; +static void arch_cache_info(int *isize, int *dsize) +{ + uint64_t ctr; + + /* + * The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, + * but (at least under Linux) these are marked protected by the + * kernel. However, CTR_EL0 contains the minimum linesize in the + * entire hierarchy, and is used by userspace cache flushing. + * + * We will also use this value in flush_idcache_range. + */ + asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); + save_ctr_el0 = ctr; + + if (*isize == 0 || *dsize == 0) { + if (*isize == 0) { + *isize = 4 << (ctr & 0xf); + } + if (*dsize == 0) { + *dsize = 4 << ((ctr >> 16) & 0xf); + } + } +} + +#elif defined(_ARCH_PPC) && defined(__linux__) +# include "elf.h" + +static void arch_cache_info(int *isize, int *dsize) +{ + if (*isize == 0) { + *isize = qemu_getauxval(AT_ICACHEBSIZE); + } + if (*dsize == 0) { + *dsize = qemu_getauxval(AT_DCACHEBSIZE); + } + have_coherent_icache = qemu_getauxval(AT_HWCAP) & PPC_FEATURE_ICACHE_SNOOP; +} + +#else +static void arch_cache_info(int *isize, int *dsize) { } +#endif /* arch_cache_info */ + +/* + * ... and if all else fails ... + */ + +static void fallback_cache_info(int *isize, int *dsize) +{ + /* If we can only find one of the two, assume they're the same. */ + if (*isize) { + if (*dsize) { + /* Success! */ + } else { + *dsize = *isize; + } + } else if (*dsize) { + *isize = *dsize; + } else { +#if defined(_ARCH_PPC) + /* + * For PPC, we're going to use the cache sizes computed for + * flush_idcache_range. Which means that we must use the + * architecture minimum. + */ + *isize = *dsize = 16; +#else + /* Otherwise, 64 bytes is not uncommon. */ + *isize = *dsize = 64; +#endif + } +} + +static void __attribute__((constructor)) init_cache_info(void) +{ + int isize = 0, dsize = 0; + + sys_cache_info(&isize, &dsize); + arch_cache_info(&isize, &dsize); + fallback_cache_info(&isize, &dsize); + + assert((isize & (isize - 1)) == 0); + assert((dsize & (dsize - 1)) == 0); + + qemu_icache_linesize = isize; + qemu_icache_linesize_log = ctz32(isize); + qemu_dcache_linesize = dsize; + qemu_dcache_linesize_log = ctz32(dsize); + + qatomic64_init(); +} + + +/* + * Architecture (+ OS) specific cache flushing mechanisms. + */ + #if defined(__i386__) || defined(__x86_64__) || defined(__s390__) /* Caches are coherent and do not require flushing; symbol inline. */ @@ -28,17 +238,6 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) } #else -/* - * TODO: unify this with cacheinfo.c. - * We want to save the whole contents of CTR_EL0, so that we - * have more than the linesize, but also IDC and DIC. - */ -static uint64_t save_ctr_el0; -static void __attribute__((constructor)) init_ctr_el0(void) -{ - asm volatile("mrs\t%0, ctr_el0" : "=r"(save_ctr_el0)); -} - /* * This is a copy of gcc's __aarch64_sync_cache_range, modified * to fit this three-operand interface. @@ -48,8 +247,8 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) const unsigned CTR_IDC = 1u << 28; const unsigned CTR_DIC = 1u << 29; const uint64_t ctr_el0 = save_ctr_el0; - const uintptr_t icache_lsize = 4 << extract64(ctr_el0, 0, 4); - const uintptr_t dcache_lsize = 4 << extract64(ctr_el0, 16, 4); + const uintptr_t icache_lsize = qemu_icache_linesize; + const uintptr_t dcache_lsize = qemu_dcache_linesize; uintptr_t p; /* @@ -104,8 +303,24 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) { uintptr_t p, b, e; - size_t dsize = qemu_dcache_linesize; - size_t isize = qemu_icache_linesize; + size_t dsize, isize; + + /* + * Some processors have coherent caches and support a simplified + * flushing procedure. See + * POWER9 UM, 4.6.2.2 Instruction Cache Block Invalidate (icbi) + * https://ibm.ent.box.com/s/tmklq90ze7aj8f4n32er1mu3sy9u8k3k + */ + if (have_coherent_icache) { + asm volatile ("sync\n\t" + "icbi 0,%0\n\t" + "isync" + : : "r"(rx) : "memory"); + return; + } + + dsize = qemu_dcache_linesize; + isize = qemu_icache_linesize; b = rw & ~(dsize - 1); e = (rw + len + dsize - 1) & ~(dsize - 1); diff --git a/util/cacheinfo.c b/util/cacheinfo.c deleted file mode 100644 index ab1644d490f..00000000000 --- a/util/cacheinfo.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * cacheinfo.c - helpers to query the host about its caches - * - * Copyright (C) 2017, Emilio G. Cota - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/host-utils.h" -#include "qemu/atomic.h" -#include "qemu/cacheinfo.h" - -int qemu_icache_linesize = 0; -int qemu_icache_linesize_log; -int qemu_dcache_linesize = 0; -int qemu_dcache_linesize_log; - -/* - * Operating system specific detection mechanisms. - */ - -#if defined(_WIN32) - -static void sys_cache_info(int *isize, int *dsize) -{ - SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; - DWORD size = 0; - BOOL success; - size_t i, n; - - /* Check for the required buffer size first. Note that if the zero - size we use for the probe results in success, then there is no - data available; fail in that case. */ - success = GetLogicalProcessorInformation(0, &size); - if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - return; - } - - n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); - if (!GetLogicalProcessorInformation(buf, &size)) { - goto fail; - } - - for (i = 0; i < n; i++) { - if (buf[i].Relationship == RelationCache - && buf[i].Cache.Level == 1) { - switch (buf[i].Cache.Type) { - case CacheUnified: - *isize = *dsize = buf[i].Cache.LineSize; - break; - case CacheInstruction: - *isize = buf[i].Cache.LineSize; - break; - case CacheData: - *dsize = buf[i].Cache.LineSize; - break; - default: - break; - } - } - } - fail: - g_free(buf); -} - -#elif defined(__APPLE__) -# include -static void sys_cache_info(int *isize, int *dsize) -{ - /* There's only a single sysctl for both I/D cache line sizes. */ - long size; - size_t len = sizeof(size); - if (!sysctlbyname("hw.cachelinesize", &size, &len, NULL, 0)) { - *isize = *dsize = size; - } -} -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -# include -static void sys_cache_info(int *isize, int *dsize) -{ - /* There's only a single sysctl for both I/D cache line sizes. */ - int size; - size_t len = sizeof(size); - if (!sysctlbyname("machdep.cacheline_size", &size, &len, NULL, 0)) { - *isize = *dsize = size; - } -} -#else -/* POSIX */ - -static void sys_cache_info(int *isize, int *dsize) -{ -# ifdef _SC_LEVEL1_ICACHE_LINESIZE - int tmp_isize = (int) sysconf(_SC_LEVEL1_ICACHE_LINESIZE); - if (tmp_isize > 0) { - *isize = tmp_isize; - } -# endif -# ifdef _SC_LEVEL1_DCACHE_LINESIZE - int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); - if (tmp_dsize > 0) { - *dsize = tmp_dsize; - } -# endif -} -#endif /* sys_cache_info */ - -/* - * Architecture (+ OS) specific detection mechanisms. - */ - -#if defined(__aarch64__) - -static void arch_cache_info(int *isize, int *dsize) -{ - if (*isize == 0 || *dsize == 0) { - uint64_t ctr; - - /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, - but (at least under Linux) these are marked protected by the - kernel. However, CTR_EL0 contains the minimum linesize in the - entire hierarchy, and is used by userspace cache flushing. */ - asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); - if (*isize == 0) { - *isize = 4 << (ctr & 0xf); - } - if (*dsize == 0) { - *dsize = 4 << ((ctr >> 16) & 0xf); - } - } -} - -#elif defined(_ARCH_PPC) && defined(__linux__) -# include "elf.h" - -static void arch_cache_info(int *isize, int *dsize) -{ - if (*isize == 0) { - *isize = qemu_getauxval(AT_ICACHEBSIZE); - } - if (*dsize == 0) { - *dsize = qemu_getauxval(AT_DCACHEBSIZE); - } -} - -#else -static void arch_cache_info(int *isize, int *dsize) { } -#endif /* arch_cache_info */ - -/* - * ... and if all else fails ... - */ - -static void fallback_cache_info(int *isize, int *dsize) -{ - /* If we can only find one of the two, assume they're the same. */ - if (*isize) { - if (*dsize) { - /* Success! */ - } else { - *dsize = *isize; - } - } else if (*dsize) { - *isize = *dsize; - } else { -#if defined(_ARCH_PPC) - /* - * For PPC, we're going to use the cache sizes computed for - * flush_idcache_range. Which means that we must use the - * architecture minimum. - */ - *isize = *dsize = 16; -#else - /* Otherwise, 64 bytes is not uncommon. */ - *isize = *dsize = 64; -#endif - } -} - -static void __attribute__((constructor)) init_cache_info(void) -{ - int isize = 0, dsize = 0; - - sys_cache_info(&isize, &dsize); - arch_cache_info(&isize, &dsize); - fallback_cache_info(&isize, &dsize); - - assert((isize & (isize - 1)) == 0); - assert((dsize & (dsize - 1)) == 0); - - qemu_icache_linesize = isize; - qemu_icache_linesize_log = ctz32(isize); - qemu_dcache_linesize = dsize; - qemu_dcache_linesize_log = ctz32(dsize); - - qatomic64_init(); -} diff --git a/util/cutils.c b/util/cutils.c index a58bcfd80e7..cb43dda213c 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -35,6 +35,15 @@ #include #endif +#ifdef __HAIKU__ +#include +#endif + +#ifdef G_OS_WIN32 +#include +#include +#endif + #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -872,6 +881,25 @@ int parse_debug_env(const char *name, int max, int initial) return debug; } +const char *si_prefix(unsigned int exp10) +{ + static const char *prefixes[] = { + "a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E" + }; + + exp10 += 18; + assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes)); + return prefixes[exp10 / 3]; +} + +const char *iec_binary_prefix(unsigned int exp2) +{ + static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; + + assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes)); + return prefixes[exp2 / 10]; +} + /* * Return human readable string for size @val. * @val can be anything that uint64_t allows (no more than "16 EiB"). @@ -880,7 +908,6 @@ int parse_debug_env(const char *name, int max, int initial) */ char *size_to_str(uint64_t val) { - static const char *suffixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; uint64_t div; int i; @@ -891,25 +918,23 @@ char *size_to_str(uint64_t val) * (see e41b509d68afb1f for more info) */ frexp(val / (1000.0 / 1024.0), &i); - i = (i - 1) / 10; - div = 1ULL << (i * 10); + i = (i - 1) / 10 * 10; + div = 1ULL << i; - return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]); + return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i)); } char *freq_to_str(uint64_t freq_hz) { - static const char *const suffixes[] = { "", "K", "M", "G", "T", "P", "E" }; double freq = freq_hz; - size_t idx = 0; + size_t exp10 = 0; while (freq >= 1000.0) { freq /= 1000.0; - idx++; + exp10 += 3; } - assert(idx < ARRAY_SIZE(suffixes)); - return g_strdup_printf("%0.3g %sHz", freq, suffixes[idx]); + return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10)); } int qemu_pstrcmp0(const char **str1, const char **str2) @@ -1058,31 +1083,52 @@ char *get_relocated_path(const char *dir) /* Fail if qemu_init_exec_dir was not called. */ assert(exec_dir[0]); - if (!starts_with_prefix(dir) || !starts_with_prefix(bindir)) { - return g_strdup(dir); - } result = g_string_new(exec_dir); + g_string_append(result, "/qemu-bundle"); + if (access(result->str, R_OK) == 0) { +#ifdef G_OS_WIN32 + size_t size = mbsrtowcs(NULL, &dir, 0, &(mbstate_t){0}) + 1; + PWSTR wdir = g_new(WCHAR, size); + mbsrtowcs(wdir, &dir, size, &(mbstate_t){0}); + + PCWSTR wdir_skipped_root; + PathCchSkipRoot(wdir, &wdir_skipped_root); + + 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 { + g_string_assign(result, exec_dir); + + /* Advance over common components. */ + len_dir = len_bindir = prefix_len; + do { + dir += len_dir; + bindir += len_bindir; + dir = next_component(dir, &len_dir); + bindir = next_component(bindir, &len_bindir); + } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); + + /* Ascend from bindir to the common prefix with dir. */ + while (len_bindir) { + bindir += len_bindir; + g_string_append(result, "/.."); + bindir = next_component(bindir, &len_bindir); + } - /* Advance over common components. */ - len_dir = len_bindir = prefix_len; - do { - dir += len_dir; - bindir += len_bindir; - dir = next_component(dir, &len_dir); - bindir = next_component(bindir, &len_bindir); - } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); - - /* Ascend from bindir to the common prefix with dir. */ - while (len_bindir) { - bindir += len_bindir; - g_string_append(result, "/.."); - bindir = next_component(bindir, &len_bindir); + if (*dir) { + assert(G_IS_DIR_SEPARATOR(dir[-1])); + g_string_append(result, dir - 1); + } } - if (*dir) { - assert(G_IS_DIR_SEPARATOR(dir[-1])); - g_string_append(result, dir - 1); - } return g_string_free(result, false); } diff --git a/util/hbitmap.c b/util/hbitmap.c index ea989e1f0e5..297db35fb11 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -873,11 +873,6 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) } } -bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b) -{ - return (a->orig_size == b->orig_size); -} - /** * hbitmap_sparse_merge: performs dst = dst | src * works with differing granularities. @@ -901,28 +896,24 @@ static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src) * Given HBitmaps A and B, let R := A (BITOR) B. * Bitmaps A and B will not be modified, * except when bitmap R is an alias of A or B. - * - * @return true if the merge was successful, - * false if it was not attempted. + * Bitmaps must have same size. */ -bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) +void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) { int i; uint64_t j; - if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) { - return false; - } - assert(hbitmap_can_merge(b, result)); + assert(a->orig_size == result->orig_size); + assert(b->orig_size == result->orig_size); if ((!hbitmap_count(a) && result == b) || (!hbitmap_count(b) && result == a)) { - return true; + return; } if (!hbitmap_count(a) && !hbitmap_count(b)) { hbitmap_reset_all(result); - return true; + return; } if (a->granularity != b->granularity) { @@ -935,7 +926,7 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) if (b != result) { hbitmap_sparse_merge(result, b); } - return true; + return; } /* This merge is O(size), as BITS_PER_LONG and HBITMAP_LEVELS are constant. @@ -951,8 +942,6 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) /* Recompute the dirty count */ result->count = hb_count_between(result, 0, result->size - 1); - - return true; } char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) diff --git a/util/host-utils.c b/util/host-utils.c index 96d5dc0bed2..fb91bcba823 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -266,3 +266,183 @@ void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) *plow = *plow << shift; } } + +/* + * Unsigned 256-by-128 division. + * Returns the remainder via r. + * Returns lower 128 bit of quotient. + * Needs a normalized divisor (most significant bit set to 1). + * + * Adapted from include/qemu/host-utils.h udiv_qrnnd, + * from the GNU Multi Precision Library - longlong.h __udiv_qrnnd + * (https://gmplib.org/repo/gmp/file/tip/longlong.h) + * + * Licensed under the GPLv2/LGPLv3 + */ +static Int128 udiv256_qrnnd(Int128 *r, Int128 n1, Int128 n0, Int128 d) +{ + Int128 d0, d1, q0, q1, r1, r0, m; + uint64_t mp0, mp1; + + d0 = int128_make64(int128_getlo(d)); + d1 = int128_make64(int128_gethi(d)); + + r1 = int128_remu(n1, d1); + q1 = int128_divu(n1, d1); + mp0 = int128_getlo(q1); + mp1 = int128_gethi(q1); + mulu128(&mp0, &mp1, int128_getlo(d0)); + m = int128_make128(mp0, mp1); + r1 = int128_make128(int128_gethi(n0), int128_getlo(r1)); + if (int128_ult(r1, m)) { + q1 = int128_sub(q1, int128_one()); + r1 = int128_add(r1, d); + if (int128_uge(r1, d)) { + if (int128_ult(r1, m)) { + q1 = int128_sub(q1, int128_one()); + r1 = int128_add(r1, d); + } + } + } + r1 = int128_sub(r1, m); + + r0 = int128_remu(r1, d1); + q0 = int128_divu(r1, d1); + mp0 = int128_getlo(q0); + mp1 = int128_gethi(q0); + mulu128(&mp0, &mp1, int128_getlo(d0)); + m = int128_make128(mp0, mp1); + r0 = int128_make128(int128_getlo(n0), int128_getlo(r0)); + if (int128_ult(r0, m)) { + q0 = int128_sub(q0, int128_one()); + r0 = int128_add(r0, d); + if (int128_uge(r0, d)) { + if (int128_ult(r0, m)) { + q0 = int128_sub(q0, int128_one()); + r0 = int128_add(r0, d); + } + } + } + r0 = int128_sub(r0, m); + + *r = r0; + return int128_or(int128_lshift(q1, 64), q0); +} + +/* + * Unsigned 256-by-128 division. + * Returns the remainder. + * Returns quotient via plow and phigh. + * Also returns the remainder via the function return value. + */ +Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor) +{ + Int128 dhi = *phigh; + Int128 dlo = *plow; + Int128 rem, dhighest; + int sh; + + if (!int128_nz(divisor) || !int128_nz(dhi)) { + *plow = int128_divu(dlo, divisor); + *phigh = int128_zero(); + return int128_remu(dlo, divisor); + } else { + sh = clz128(divisor); + + if (int128_ult(dhi, divisor)) { + if (sh != 0) { + /* normalize the divisor, shifting the dividend accordingly */ + divisor = int128_lshift(divisor, sh); + dhi = int128_or(int128_lshift(dhi, sh), + int128_urshift(dlo, (128 - sh))); + dlo = int128_lshift(dlo, sh); + } + + *phigh = int128_zero(); + *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); + } else { + if (sh != 0) { + /* normalize the divisor, shifting the dividend accordingly */ + divisor = int128_lshift(divisor, sh); + dhighest = int128_rshift(dhi, (128 - sh)); + dhi = int128_or(int128_lshift(dhi, sh), + int128_urshift(dlo, (128 - sh))); + dlo = int128_lshift(dlo, sh); + + *phigh = udiv256_qrnnd(&dhi, dhighest, dhi, divisor); + } else { + /* + * dhi >= divisor + * Since the MSB of divisor is set (sh == 0), + * (dhi - divisor) < divisor + * + * Thus, the high part of the quotient is 1, and we can + * calculate the low part with a single call to udiv_qrnnd + * after subtracting divisor from dhi + */ + dhi = int128_sub(dhi, divisor); + *phigh = int128_one(); + } + + *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); + } + + /* + * since the dividend/divisor might have been normalized, + * the remainder might also have to be shifted back + */ + rem = int128_urshift(rem, sh); + return rem; + } +} + +/* + * Signed 256-by-128 division. + * Returns quotient via plow and phigh. + * Also returns the remainder via the function return value. + */ +Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor) +{ + bool neg_quotient = false, neg_remainder = false; + Int128 unsig_hi = *phigh, unsig_lo = *plow; + Int128 rem; + + if (!int128_nonneg(*phigh)) { + neg_quotient = !neg_quotient; + neg_remainder = !neg_remainder; + + if (!int128_nz(unsig_lo)) { + unsig_hi = int128_neg(unsig_hi); + } else { + unsig_hi = int128_not(unsig_hi); + unsig_lo = int128_neg(unsig_lo); + } + } + + if (!int128_nonneg(divisor)) { + neg_quotient = !neg_quotient; + + divisor = int128_neg(divisor); + } + + rem = divu256(&unsig_lo, &unsig_hi, divisor); + + if (neg_quotient) { + if (!int128_nz(unsig_lo)) { + *phigh = int128_neg(unsig_hi); + *plow = int128_zero(); + } else { + *phigh = int128_not(unsig_hi); + *plow = int128_neg(unsig_lo); + } + } else { + *phigh = unsig_hi; + *plow = unsig_lo; + } + + if (neg_remainder) { + return int128_neg(rem); + } else { + return rem; + } +} diff --git a/util/iova-tree.c b/util/iova-tree.c index 6dff29c1f62..fee530a5791 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -164,15 +164,13 @@ void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) g_tree_foreach(tree->tree, iova_tree_traverse, iterator); } -int iova_tree_remove(IOVATree *tree, const DMAMap *map) +void iova_tree_remove(IOVATree *tree, const DMAMap *map) { const DMAMap *overlap; while ((overlap = iova_tree_find(tree, map))) { g_tree_remove(tree->tree, overlap); } - - return IOVA_OK; } /** diff --git a/util/meson.build b/util/meson.build index 8f16018cd43..5e282130df8 100644 --- a/util/meson.build +++ b/util/meson.build @@ -23,11 +23,12 @@ util_ss.add(when: 'CONFIG_WIN32', if_true: files('event_notifier-win32.c')) util_ss.add(when: 'CONFIG_WIN32', if_true: files('oslib-win32.c')) util_ss.add(when: 'CONFIG_WIN32', if_true: files('qemu-thread-win32.c')) util_ss.add(when: 'CONFIG_WIN32', if_true: winmm) +util_ss.add(when: 'CONFIG_WIN32', if_true: pathcch) util_ss.add(files('envlist.c', 'path.c', 'module.c')) util_ss.add(files('host-utils.c')) util_ss.add(files('bitmap.c', 'bitops.c')) util_ss.add(files('fifo8.c')) -util_ss.add(files('cacheinfo.c', 'cacheflush.c')) +util_ss.add(files('cacheflush.c')) util_ss.add(files('error.c', 'error-report.c')) util_ss.add(files('qemu-print.c')) util_ss.add(files('id.c')) @@ -85,6 +86,7 @@ if have_block util_ss.add(files('block-helpers.c')) util_ss.add(files('qemu-coroutine-sleep.c')) util_ss.add(files('qemu-co-shared-resource.c')) + util_ss.add(files('qemu-co-timeout.c')) util_ss.add(files('thread-pool.c', 'qemu-timer.c')) util_ss.add(files('readline.c')) util_ss.add(files('throttle.c')) diff --git a/util/module.c b/util/module.c index 6bb4ad915a1..8ddb0e18f51 100644 --- a/util/module.c +++ b/util/module.c @@ -274,7 +274,6 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) dirs[n_dirs++] = g_strdup_printf("%s", search_dir); } dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); - dirs[n_dirs++] = g_strdup(qemu_get_exec_dir()); #ifdef CONFIG_MODULE_UPGRADES version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION), diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 7a34c1657c0..bffec18869e 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -62,10 +62,6 @@ #include #endif -#ifdef __HAIKU__ -#include -#endif - #include "qemu/mmap-alloc.h" #ifdef CONFIG_DEBUG_STACK_USAGE diff --git a/util/qemu-co-timeout.c b/util/qemu-co-timeout.c new file mode 100644 index 00000000000..00cd335649f --- /dev/null +++ b/util/qemu-co-timeout.c @@ -0,0 +1,89 @@ +/* + * Helper functionality for distributing a fixed total amount of + * an abstract resource among multiple coroutines. + * + * Copyright (c) 2022 Virtuozzo International GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/coroutine.h" +#include "block/aio.h" + +typedef struct QemuCoTimeoutState { + CoroutineEntry *entry; + void *opaque; + QemuCoSleep sleep_state; + bool marker; + CleanupFunc *clean; +} QemuCoTimeoutState; + +static void coroutine_fn qemu_co_timeout_entry(void *opaque) +{ + QemuCoTimeoutState *s = opaque; + + s->entry(s->opaque); + + if (s->marker) { + assert(!s->sleep_state.to_wake); + /* .marker set by qemu_co_timeout, it have been failed */ + if (s->clean) { + s->clean(s->opaque); + } + g_free(s); + } else { + s->marker = true; + qemu_co_sleep_wake(&s->sleep_state); + } +} + +int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, + uint64_t timeout_ns, CleanupFunc clean) +{ + QemuCoTimeoutState *s; + Coroutine *co; + + if (timeout_ns == 0) { + entry(opaque); + return 0; + } + + s = g_new(QemuCoTimeoutState, 1); + *s = (QemuCoTimeoutState) { + .entry = entry, + .opaque = opaque, + .clean = clean + }; + + co = qemu_coroutine_create(qemu_co_timeout_entry, s); + + aio_co_enter(qemu_get_current_aio_context(), co); + qemu_co_sleep_ns_wakeable(&s->sleep_state, QEMU_CLOCK_REALTIME, timeout_ns); + + if (s->marker) { + /* .marker set by qemu_co_timeout_entry, success */ + g_free(s); + return 0; + } + + /* Don't free s, as we can't cancel qemu_co_timeout_entry execution */ + s->marker = true; + return -ETIMEDOUT; +} diff --git a/util/qemu-timer.c b/util/qemu-timer.c index a670a578818..6a0de33dd2b 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -261,6 +261,9 @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) } QLIST_FOREACH(timer_list, &clock->timerlists, list) { + if (!qatomic_read(&timer_list->active_timers)) { + continue; + } qemu_mutex_lock(&timer_list->active_timers_lock); ts = timer_list->active_timers; /* Skip all external timers */