diff options
154 files changed, 4795 insertions, 3286 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index f53c519447..f4bf49b704 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -67,7 +67,7 @@ windows_msys2_task: CIRRUS_SHELL: powershell MSYS: winsymlinks:nativestrict MSYSTEM: MINGW64 - MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2021-01-05/msys2-base-x86_64-20210105.sfx.exe + MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2021-04-19/msys2-base-x86_64-20210419.sfx.exe MSYS2_FINGERPRINT: 0 MSYS2_PACKAGES: " diffutils git grep make pkg-config sed @@ -130,7 +130,7 @@ windows_msys2_task: taskkill /F /FI "MODULES eq msys-2.0.dll" tasklist C:\tools\msys64\usr\bin\bash.exe -lc "mv -f /etc/pacman.conf.pacnew /etc/pacman.conf || true" - C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Suu --overwrite=*" + C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu --overwrite=*" Write-Output "Core install time taken: $((Get-Date).Subtract($start_time))" $start_time = Get-Date diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml index 33e4046e23..4ef76d1f54 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -1,4 +1,4 @@ -.container_job_template: &container_job_definition +.container_job_template: image: docker:stable stage: containers services: @@ -22,230 +22,230 @@ - docker logout amd64-alpine-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: alpine amd64-centos7-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: centos7 amd64-centos8-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: centos8 amd64-debian10-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: debian10 amd64-debian11-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: debian11 alpha-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-alpha-cross amd64-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-amd64-cross amd64-debian-user-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-all-test-cross amd64-debian-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-amd64 arm64-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-arm64-cross arm64-test-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian11-container'] variables: NAME: debian-arm64-test-cross armel-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-armel-cross armhf-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-armhf-cross hppa-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-hppa-cross m68k-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-m68k-cross mips64-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-mips64-cross mips64el-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-mips64el-cross mips-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-mips-cross mipsel-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-mipsel-cross powerpc-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-powerpc-cross ppc64-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-ppc64-cross ppc64el-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-ppc64el-cross riscv64-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-riscv64-cross s390x-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-s390x-cross sh4-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-sh4-cross sparc64-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-sparc64-cross tricore-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template stage: containers-layer2 needs: ['amd64-debian10-container'] variables: NAME: debian-tricore-cross xtensa-debian-cross-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: debian-xtensa-cross cris-fedora-cross-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: fedora-cris-cross amd64-fedora-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: fedora i386-fedora-cross-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: fedora-i386-cross win32-fedora-cross-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: fedora-win32-cross win64-fedora-cross-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: fedora-win64-cross amd64-ubuntu1804-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: ubuntu1804 amd64-ubuntu2004-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: ubuntu2004 amd64-ubuntu-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: ubuntu amd64-opensuse-leap-container: - <<: *container_job_definition + extends: .container_job_template variables: NAME: opensuse-leap diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9876f73040..24f300aace 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ include: - local: '/.gitlab-ci.d/containers.yml' - local: '/.gitlab-ci.d/crossbuilds.yml' -.native_build_job_template: &native_build_job_definition +.native_build_job_template: stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest before_script: @@ -41,7 +41,7 @@ include: make -j"$JOBS" $MAKE_CHECK_ARGS ; fi -.native_test_job_template: &native_test_job_definition +.native_test_job_template: stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest script: @@ -83,7 +83,7 @@ include: - du -chs ${CI_PROJECT_DIR}/avocado-cache build-system-alpine: - <<: *native_build_job_definition + extends: .native_build_job_template needs: - job: amd64-alpine-container variables: @@ -99,7 +99,7 @@ build-system-alpine: - build check-system-alpine: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-alpine artifacts: true @@ -108,7 +108,7 @@ check-system-alpine: MAKE_CHECK_ARGS: check acceptance-system-alpine: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-alpine artifacts: true @@ -118,7 +118,7 @@ acceptance-system-alpine: <<: *acceptance_definition build-system-ubuntu: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-ubuntu2004-container variables: @@ -133,7 +133,7 @@ build-system-ubuntu: - build check-system-ubuntu: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-ubuntu artifacts: true @@ -142,7 +142,7 @@ check-system-ubuntu: MAKE_CHECK_ARGS: check acceptance-system-ubuntu: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-ubuntu artifacts: true @@ -152,7 +152,7 @@ acceptance-system-ubuntu: <<: *acceptance_definition build-system-debian: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-container variables: @@ -167,7 +167,7 @@ build-system-debian: - build check-system-debian: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-debian artifacts: true @@ -176,7 +176,7 @@ check-system-debian: MAKE_CHECK_ARGS: check acceptance-system-debian: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-debian artifacts: true @@ -186,7 +186,7 @@ acceptance-system-debian: <<: *acceptance_definition build-system-fedora: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-fedora-container variables: @@ -202,7 +202,7 @@ build-system-fedora: - build check-system-fedora: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-fedora artifacts: true @@ -211,7 +211,7 @@ check-system-fedora: MAKE_CHECK_ARGS: check acceptance-system-fedora: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-fedora artifacts: true @@ -221,7 +221,7 @@ acceptance-system-fedora: <<: *acceptance_definition build-system-centos: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos8-container variables: @@ -237,7 +237,7 @@ build-system-centos: - build check-system-centos: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-centos artifacts: true @@ -246,7 +246,7 @@ check-system-centos: MAKE_CHECK_ARGS: check acceptance-system-centos: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-centos artifacts: true @@ -256,7 +256,7 @@ acceptance-system-centos: <<: *acceptance_definition build-system-opensuse: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-opensuse-leap-container variables: @@ -270,7 +270,7 @@ build-system-opensuse: - build check-system-opensuse: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-opensuse artifacts: true @@ -279,7 +279,7 @@ check-system-opensuse: MAKE_CHECK_ARGS: check acceptance-system-opensuse: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-system-opensuse artifacts: true @@ -290,7 +290,7 @@ acceptance-system-opensuse: build-disabled: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-fedora-container variables: @@ -376,7 +376,7 @@ build-disabled: # Also use a different coroutine implementation (which is only really of # interest to KVM users, i.e. with TCG disabled) build-tcg-disabled: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos8-container variables: @@ -399,7 +399,7 @@ build-tcg-disabled: 260 261 262 263 264 270 272 273 277 279 build-user: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -408,7 +408,7 @@ build-user: MAKE_CHECK_ARGS: check-tcg build-user-static: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -418,7 +418,7 @@ build-user-static: # Only build the softmmu targets we have check-tcg tests for build-some-softmmu: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -431,7 +431,7 @@ build-some-softmmu: # we skip sparc64-linux-user until it has been fixed somewhat # we skip cris-linux-user as it doesn't use the common run loop build-user-plugins: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -441,7 +441,7 @@ build-user-plugins: timeout: 1h 30m build-user-centos7: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos7-container variables: @@ -450,7 +450,7 @@ build-user-centos7: MAKE_CHECK_ARGS: check-tcg build-some-softmmu-plugins: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -460,7 +460,7 @@ build-some-softmmu-plugins: MAKE_CHECK_ARGS: check-tcg clang-system: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-fedora-container variables: @@ -472,7 +472,7 @@ clang-system: MAKE_CHECK_ARGS: check-qtest check-tcg clang-user: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -494,7 +494,7 @@ clang-user: # Split in three sets of build/check/acceptance to limit the execution time of each # job build-cfi-aarch64: - <<: *native_build_job_definition + extends: .native_build_job_template needs: - job: amd64-fedora-container variables: @@ -512,7 +512,7 @@ build-cfi-aarch64: - build check-cfi-aarch64: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-cfi-aarch64 artifacts: true @@ -521,7 +521,7 @@ check-cfi-aarch64: MAKE_CHECK_ARGS: check acceptance-cfi-aarch64: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-cfi-aarch64 artifacts: true @@ -531,7 +531,7 @@ acceptance-cfi-aarch64: <<: *acceptance_definition build-cfi-ppc64-s390x: - <<: *native_build_job_definition + extends: .native_build_job_template needs: - job: amd64-fedora-container variables: @@ -549,7 +549,7 @@ build-cfi-ppc64-s390x: - build check-cfi-ppc64-s390x: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-cfi-ppc64-s390x artifacts: true @@ -558,7 +558,7 @@ check-cfi-ppc64-s390x: MAKE_CHECK_ARGS: check acceptance-cfi-ppc64-s390x: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-cfi-ppc64-s390x artifacts: true @@ -568,7 +568,7 @@ acceptance-cfi-ppc64-s390x: <<: *acceptance_definition build-cfi-x86_64: - <<: *native_build_job_definition + extends: .native_build_job_template needs: - job: amd64-fedora-container variables: @@ -586,7 +586,7 @@ build-cfi-x86_64: - build check-cfi-x86_64: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-cfi-x86_64 artifacts: true @@ -595,7 +595,7 @@ check-cfi-x86_64: MAKE_CHECK_ARGS: check acceptance-cfi-x86_64: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-cfi-x86_64 artifacts: true @@ -605,7 +605,7 @@ acceptance-cfi-x86_64: <<: *acceptance_definition tsan-build: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-ubuntu2004-container variables: @@ -617,7 +617,7 @@ tsan-build: # These targets are on the way out build-deprecated: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -633,7 +633,7 @@ build-deprecated: # We split the check-tcg step as test failures are expected but we still # want to catch the build breaking. check-deprecated: - <<: *native_test_job_definition + extends: .native_test_job_template needs: - job: build-deprecated artifacts: true @@ -644,7 +644,7 @@ check-deprecated: # gprof/gcov are GCC features gprof-gcov: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-ubuntu2004-container variables: @@ -657,7 +657,7 @@ gprof-gcov: - ${CI_PROJECT_DIR}/scripts/ci/coverage-summary.sh build-oss-fuzz: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-fedora-container variables: @@ -677,7 +677,7 @@ build-oss-fuzz: - cd build-oss-fuzz && make check-qtest-i386 check-unit build-tci: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-user-cross-container variables: @@ -702,7 +702,7 @@ build-tci: # Alternate coroutines implementations are only really of interest to KVM users # However we can't test against KVM on Gitlab-CI so we can only run unit tests build-coroutine-sigaltstack: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-ubuntu2004-container variables: @@ -716,7 +716,7 @@ build-coroutine-sigaltstack: # These jobs test old gcrypt and nettle from RHEL7 # which had some API differences. crypto-old-nettle: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos7-container variables: @@ -726,7 +726,7 @@ crypto-old-nettle: MAKE_CHECK_ARGS: check crypto-old-gcrypt: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos7-container variables: @@ -736,7 +736,7 @@ crypto-old-gcrypt: MAKE_CHECK_ARGS: check crypto-only-gnutls: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos7-container variables: @@ -748,7 +748,7 @@ crypto-only-gnutls: # Check our reduced build configurations build-without-default-devices: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-centos8-container variables: @@ -756,7 +756,7 @@ build-without-default-devices: CONFIGURE_ARGS: --without-default-devices --disable-user build-without-default-features: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-container variables: @@ -806,7 +806,7 @@ build-libvhost-user: # No targets are built here, just tools, docs, and unit tests. This # also feeds into the eventual documentation deployment steps later build-tools-and-docs-debian: - <<: *native_build_job_definition + extends: .native_build_job_template needs: job: amd64-debian-container variables: diff --git a/MAINTAINERS b/MAINTAINERS index 6f73315dcc..515316f7b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2024,6 +2024,12 @@ S: Maintained F: hw/net/tulip.c F: hw/net/tulip.h +pca954x +M: Patrick Venture <venture@google.com> +S: Maintained +F: hw/i2c/i2c_mux_pca954x.c +F: include/hw/i2c/i2c_mux_pca954x.h + Generic Loader M: Alistair Francis <alistair@alistair23.me> S: Maintained diff --git a/Makefile b/Makefile index bcbbec71a1..4cab10a2a4 100644 --- a/Makefile +++ b/Makefile @@ -213,7 +213,7 @@ qemu-%.tar.bz2: distclean: clean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : - rm -f config-host.mak config-host.h* + rm -f config-host.mak config-host.h* config-poison.h rm -f tests/tcg/config-*.mak rm -f config-all-disas.mak config.status rm -f roms/seabios/config.mak roms/vgabios/config.mak diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 521da4a813..ac7d28c251 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -1073,9 +1073,8 @@ void HELPER(gvec_ssadd32)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(int32_t)) { int32_t ai = *(int32_t *)(a + i); int32_t bi = *(int32_t *)(b + i); - int32_t di = ai + bi; - if (((di ^ ai) &~ (ai ^ bi)) < 0) { - /* Signed overflow. */ + int32_t di; + if (sadd32_overflow(ai, bi, &di)) { di = (di < 0 ? INT32_MAX : INT32_MIN); } *(int32_t *)(d + i) = di; @@ -1091,9 +1090,8 @@ void HELPER(gvec_ssadd64)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(int64_t)) { int64_t ai = *(int64_t *)(a + i); int64_t bi = *(int64_t *)(b + i); - int64_t di = ai + bi; - if (((di ^ ai) &~ (ai ^ bi)) < 0) { - /* Signed overflow. */ + int64_t di; + if (sadd64_overflow(ai, bi, &di)) { di = (di < 0 ? INT64_MAX : INT64_MIN); } *(int64_t *)(d + i) = di; @@ -1143,9 +1141,8 @@ void HELPER(gvec_sssub32)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(int32_t)) { int32_t ai = *(int32_t *)(a + i); int32_t bi = *(int32_t *)(b + i); - int32_t di = ai - bi; - if (((di ^ ai) & (ai ^ bi)) < 0) { - /* Signed overflow. */ + int32_t di; + if (ssub32_overflow(ai, bi, &di)) { di = (di < 0 ? INT32_MAX : INT32_MIN); } *(int32_t *)(d + i) = di; @@ -1161,9 +1158,8 @@ void HELPER(gvec_sssub64)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(int64_t)) { int64_t ai = *(int64_t *)(a + i); int64_t bi = *(int64_t *)(b + i); - int64_t di = ai - bi; - if (((di ^ ai) & (ai ^ bi)) < 0) { - /* Signed overflow. */ + int64_t di; + if (ssub64_overflow(ai, bi, &di)) { di = (di < 0 ? INT64_MAX : INT64_MIN); } *(int64_t *)(d + i) = di; @@ -1209,8 +1205,8 @@ void HELPER(gvec_usadd32)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(uint32_t)) { uint32_t ai = *(uint32_t *)(a + i); uint32_t bi = *(uint32_t *)(b + i); - uint32_t di = ai + bi; - if (di < ai) { + uint32_t di; + if (uadd32_overflow(ai, bi, &di)) { di = UINT32_MAX; } *(uint32_t *)(d + i) = di; @@ -1226,8 +1222,8 @@ void HELPER(gvec_usadd64)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(uint64_t)) { uint64_t ai = *(uint64_t *)(a + i); uint64_t bi = *(uint64_t *)(b + i); - uint64_t di = ai + bi; - if (di < ai) { + uint64_t di; + if (uadd64_overflow(ai, bi, &di)) { di = UINT64_MAX; } *(uint64_t *)(d + i) = di; @@ -1273,8 +1269,8 @@ void HELPER(gvec_ussub32)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(uint32_t)) { uint32_t ai = *(uint32_t *)(a + i); uint32_t bi = *(uint32_t *)(b + i); - uint32_t di = ai - bi; - if (ai < bi) { + uint32_t di; + if (usub32_overflow(ai, bi, &di)) { di = 0; } *(uint32_t *)(d + i) = di; @@ -1290,8 +1286,8 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) for (i = 0; i < oprsz; i += sizeof(uint64_t)) { uint64_t ai = *(uint64_t *)(a + i); uint64_t bi = *(uint64_t *)(b + i); - uint64_t di = ai - bi; - if (ai < bi) { + uint64_t di; + if (usub64_overflow(ai, bi, &di)) { di = 0; } *(uint64_t *)(d + i) = di; diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index a012adc193..e5f1063ab6 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -30,6 +30,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/sockets.h" +#include "qemu/lockable.h" #include "io/channel-socket.h" #include "sysemu/tpm_backend.h" #include "sysemu/tpm_util.h" @@ -124,31 +125,26 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, uint32_t cmd_no = cpu_to_be32(cmd); ssize_t n = sizeof(uint32_t) + msg_len_in; uint8_t *buf = NULL; - int ret = -1; - qemu_mutex_lock(&tpm->mutex); + WITH_QEMU_LOCK_GUARD(&tpm->mutex) { + buf = g_alloca(n); + memcpy(buf, &cmd_no, sizeof(cmd_no)); + memcpy(buf + sizeof(cmd_no), msg, msg_len_in); - buf = g_alloca(n); - memcpy(buf, &cmd_no, sizeof(cmd_no)); - memcpy(buf + sizeof(cmd_no), msg, msg_len_in); - - n = qemu_chr_fe_write_all(dev, buf, n); - if (n <= 0) { - goto end; - } - - if (msg_len_out != 0) { - n = qemu_chr_fe_read_all(dev, msg, msg_len_out); + n = qemu_chr_fe_write_all(dev, buf, n); if (n <= 0) { - goto end; + return -1; } - } - ret = 0; + if (msg_len_out != 0) { + n = qemu_chr_fe_read_all(dev, msg, msg_len_out); + if (n <= 0) { + return -1; + } + } + } -end: - qemu_mutex_unlock(&tpm->mutex); - return ret; + return 0; } static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, diff --git a/block.c b/block.c index 9ad725d205..75a82af641 100644 --- a/block.c +++ b/block.c @@ -400,7 +400,6 @@ BlockDriverState *bdrv_new(void) for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { QLIST_INIT(&bs->op_blockers[i]); } - notifier_with_return_list_init(&bs->before_write_notifiers); qemu_co_mutex_init(&bs->reqs_lock); qemu_mutex_init(&bs->dirty_bitmap_mutex); bs->refcnt = 1; diff --git a/block/backup.c b/block/backup.c index 6cf2f974aa..bd3614ce70 100644 --- a/block/backup.c +++ b/block/backup.c @@ -331,7 +331,7 @@ static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed) } } -static void backup_cancel(Job *job) +static void backup_cancel(Job *job, bool force) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 9cad9e1b8c..c428682272 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -29,7 +29,6 @@ typedef struct BDRVStateCOR { - bool active; BlockDriverState *bottom_bs; bool chain_frozen; } BDRVStateCOR; @@ -89,7 +88,6 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, */ bdrv_ref(bottom_bs); } - state->active = true; state->bottom_bs = bottom_bs; /* @@ -112,17 +110,6 @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { - BDRVStateCOR *s = bs->opaque; - - if (!s->active) { - /* - * While the filter is being removed - */ - *nperm = 0; - *nshared = BLK_PERM_ALL; - return; - } - *nperm = perm & PERM_PASSTHROUGH; *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; @@ -280,32 +267,14 @@ static BlockDriver bdrv_copy_on_read = { void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs) { - BdrvChild *child; - BlockDriverState *bs; BDRVStateCOR *s = cor_filter_bs->opaque; - child = bdrv_filter_child(cor_filter_bs); - if (!child) { - return; - } - bs = child->bs; - - /* Retain the BDS until we complete the graph change. */ - bdrv_ref(bs); - /* Hold a guest back from writing while permissions are being reset. */ - bdrv_drained_begin(bs); - /* Drop permissions before the graph change. */ - s->active = false; /* unfreeze, as otherwise bdrv_replace_node() will fail */ if (s->chain_frozen) { s->chain_frozen = false; bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs); } - bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort); - bdrv_replace_node(cor_filter_bs, bs, &error_abort); - - bdrv_drained_end(bs); - bdrv_unref(bs); + bdrv_drop_filter(cor_filter_bs, &error_abort); bdrv_unref(cor_filter_bs); } diff --git a/block/io.c b/block/io.c index 35b6c56efc..1e826ba9e8 100644 --- a/block/io.c +++ b/block/io.c @@ -30,6 +30,7 @@ #include "block/blockjob_int.h" #include "block/block_int.h" #include "block/coroutines.h" +#include "block/write-threshold.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -2008,8 +2009,8 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, } else { assert(child->perm & BLK_PERM_WRITE); } - return notifier_with_return_list_notify(&bs->before_write_notifiers, - req); + bdrv_write_threshold_check_write(bs, offset, bytes); + return 0; case BDRV_TRACKED_TRUNCATE: assert(child->perm & BLK_PERM_RESIZE); return 0; @@ -3164,12 +3165,6 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) return true; } -void bdrv_add_before_write_notifier(BlockDriverState *bs, - NotifierWithReturn *notifier) -{ - notifier_with_return_list_add(&bs->before_write_notifiers, notifier); -} - void bdrv_io_plug(BlockDriverState *bs) { BdrvChild *child; diff --git a/block/mirror.c b/block/mirror.c index 840b8e8c15..019f6deaa5 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1178,12 +1178,14 @@ static bool mirror_drained_poll(BlockJob *job) return !!s->in_flight; } -static void mirror_cancel(Job *job) +static void mirror_cancel(Job *job, bool force) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockDriverState *target = blk_bs(s->target); - bdrv_cancel_in_flight(target); + if (force || !job_is_ready(job)) { + bdrv_cancel_in_flight(target); + } } static const BlockJobDriver mirror_job_driver = { diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index ebf1033f31..3e6670c963 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -557,8 +557,10 @@ void hmp_eject(Monitor *mon, const QDict *qdict) void hmp_qemu_io(Monitor *mon, const QDict *qdict) { - BlockBackend *blk; + BlockBackend *blk = NULL; + BlockDriverState *bs = NULL; BlockBackend *local_blk = NULL; + AioContext *ctx = NULL; bool qdev = qdict_get_try_bool(qdict, "qdev", false); const char *device = qdict_get_str(qdict, "device"); const char *command = qdict_get_str(qdict, "command"); @@ -573,20 +575,24 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) } else { blk = blk_by_name(device); if (!blk) { - BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); - if (bs) { - blk = local_blk = blk_new(bdrv_get_aio_context(bs), - 0, BLK_PERM_ALL); - ret = blk_insert_bs(blk, bs, &err); - if (ret < 0) { - goto fail; - } - } else { + bs = bdrv_lookup_bs(NULL, device, &err); + if (!bs) { goto fail; } } } + ctx = blk ? blk_get_aio_context(blk) : bdrv_get_aio_context(bs); + aio_context_acquire(ctx); + + if (bs) { + blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL); + ret = blk_insert_bs(blk, bs, &err); + if (ret < 0) { + goto fail; + } + } + /* * Notably absent: Proper permission management. This is sad, but it seems * almost impossible to achieve without changing the semantics and thereby @@ -616,6 +622,11 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) fail: blk_unref(local_blk); + + if (ctx) { + aio_context_release(ctx); + } + hmp_handle_error(mon, err); } diff --git a/block/rbd.c b/block/rbd.c index f098a89c7b..26f64cce7c 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -113,21 +113,31 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, const char *keypairs, const char *secretid, Error **errp); +static char *qemu_rbd_strchr(char *src, char delim) +{ + char *p; + + for (p = src; *p; ++p) { + if (*p == delim) { + return p; + } + if (*p == '\\' && p[1] != '\0') { + ++p; + } + } + + return NULL; +} + + static char *qemu_rbd_next_tok(char *src, char delim, char **p) { char *end; *p = NULL; - for (end = src; *end; ++end) { - if (*end == delim) { - break; - } - if (*end == '\\' && end[1] != '\0') { - end++; - } - } - if (*end == delim) { + end = qemu_rbd_strchr(src, delim); + if (end) { *p = end + 1; *end = '\0'; } @@ -171,7 +181,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, qemu_rbd_unescape(found_str); qdict_put_str(options, "pool", found_str); - if (strchr(p, '@')) { + if (qemu_rbd_strchr(p, '@')) { image_name = qemu_rbd_next_tok(p, '@', &p); found_str = qemu_rbd_next_tok(p, ':', &p); @@ -181,7 +191,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, image_name = qemu_rbd_next_tok(p, ':', &p); } /* Check for namespace in the image_name */ - if (strchr(image_name, '/')) { + if (qemu_rbd_strchr(image_name, '/')) { found_str = qemu_rbd_next_tok(image_name, '/', &image_name); qemu_rbd_unescape(found_str); qdict_put_str(options, "namespace", found_str); diff --git a/block/write-threshold.c b/block/write-threshold.c index 85b78dc2a9..35cafbc22d 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -12,9 +12,7 @@ #include "qemu/osdep.h" #include "block/block_int.h" -#include "qemu/coroutine.h" #include "block/write-threshold.h" -#include "qemu/notify.h" #include "qapi/error.h" #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-events-block-core.h" @@ -24,82 +22,9 @@ uint64_t bdrv_write_threshold_get(const BlockDriverState *bs) return bs->write_threshold_offset; } -bool bdrv_write_threshold_is_set(const BlockDriverState *bs) -{ - return bs->write_threshold_offset > 0; -} - -static void write_threshold_disable(BlockDriverState *bs) -{ - if (bdrv_write_threshold_is_set(bs)) { - notifier_with_return_remove(&bs->write_threshold_notifier); - bs->write_threshold_offset = 0; - } -} - -uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, - const BdrvTrackedRequest *req) -{ - if (bdrv_write_threshold_is_set(bs)) { - if (req->offset > bs->write_threshold_offset) { - return (req->offset - bs->write_threshold_offset) + req->bytes; - } - if ((req->offset + req->bytes) > bs->write_threshold_offset) { - return (req->offset + req->bytes) - bs->write_threshold_offset; - } - } - return 0; -} - -static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, - void *opaque) -{ - BdrvTrackedRequest *req = opaque; - BlockDriverState *bs = req->bs; - uint64_t amount = 0; - - amount = bdrv_write_threshold_exceeded(bs, req); - if (amount > 0) { - qapi_event_send_block_write_threshold( - bs->node_name, - amount, - bs->write_threshold_offset); - - /* autodisable to avoid flooding the monitor */ - write_threshold_disable(bs); - } - - return 0; /* should always let other notifiers run */ -} - -static void write_threshold_register_notifier(BlockDriverState *bs) -{ - bs->write_threshold_notifier.notify = before_write_notify; - bdrv_add_before_write_notifier(bs, &bs->write_threshold_notifier); -} - -static void write_threshold_update(BlockDriverState *bs, - int64_t threshold_bytes) -{ - bs->write_threshold_offset = threshold_bytes; -} - void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes) { - if (bdrv_write_threshold_is_set(bs)) { - if (threshold_bytes > 0) { - write_threshold_update(bs, threshold_bytes); - } else { - write_threshold_disable(bs); - } - } else { - if (threshold_bytes > 0) { - /* avoid multiple registration */ - write_threshold_register_notifier(bs); - write_threshold_update(bs, threshold_bytes); - } - /* discard bogus disable request */ - } + bs->write_threshold_offset = threshold_bytes; } void qmp_block_set_write_threshold(const char *node_name, @@ -122,3 +47,17 @@ void qmp_block_set_write_threshold(const char *node_name, aio_context_release(aio_context); } + +void bdrv_write_threshold_check_write(BlockDriverState *bs, int64_t offset, + int64_t bytes) +{ + int64_t end = offset + bytes; + uint64_t wtr = bs->write_threshold_offset; + + if (wtr > 0 && end > wtr) { + qapi_event_send_block_write_threshold(bs->node_name, end - wtr, wtr); + + /* autodisable to avoid flooding the monitor */ + bdrv_write_threshold_set(bs, 0); + } +} diff --git a/configure b/configure index f05ca143b3..0e4233fd8a 100755 --- a/configure +++ b/configure @@ -6473,6 +6473,13 @@ if test -n "${deprecated_features}"; then echo " features: ${deprecated_features}" fi +# Create list of config switches that should be poisoned in common code... +# but filter out CONFIG_TCG and CONFIG_USER_ONLY which are special. +sed -n -e '/CONFIG_TCG/d' -e '/CONFIG_USER_ONLY/d' \ + -e '/^#define / { s///; s/ .*//; s/^/#pragma GCC poison /p; }' \ + *-config-devices.h *-config-target.h | \ + sort -u > config-poison.h + # Save the configure command line for later reuse. cat <<EOD >config.status #!/bin/sh diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c index c15d18c33f..081230da54 100644 --- a/contrib/vhost-user-input/main.c +++ b/contrib/vhost-user-input/main.c @@ -6,12 +6,13 @@ #include "qemu/osdep.h" -#include <linux/input.h> +#include <sys/ioctl.h> #include "qemu/iov.h" #include "qemu/bswap.h" #include "qemu/sockets.h" #include "libvhost-user-glib.h" +#include "standard-headers/linux/input.h" #include "standard-headers/linux/virtio_input.h" #include "qapi/error.h" @@ -113,13 +114,16 @@ vi_evdev_watch(VuDev *dev, int condition, void *data) static void vi_handle_status(VuInput *vi, virtio_input_event *event) { struct input_event evdev; + struct timeval tval; int rc; - if (gettimeofday(&evdev.time, NULL)) { + if (gettimeofday(&tval, NULL)) { perror("vi_handle_status: gettimeofday"); return; } + evdev.input_event_sec = tval.tv_sec; + evdev.input_event_usec = tval.tv_usec; evdev.type = le16toh(event->type); evdev.code = le16toh(event->code); evdev.value = le32toh(event->value); diff --git a/docs/_templates/editpage.html b/docs/_templates/editpage.html deleted file mode 100644 index 4319b0f5ac..0000000000 --- a/docs/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/docs/conf.py b/docs/conf.py index 2ee6111872..00cf66ab54 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,6 +29,7 @@ import os import sys import sphinx +from distutils.version import LooseVersion from sphinx.errors import ConfigError # Make Sphinx fail cleanly if using an old Python, rather than obscurely @@ -150,38 +151,47 @@ with open(os.path.join(qemu_docdir, 'defs.rst.inc')) as f: # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +try: + import sphinx_rtd_theme +except ImportError: + raise ConfigError( + 'The Sphinx \'sphinx_rtd_theme\' HTML theme was not found.\n' + ) + +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -# We initialize this to empty here, so the per-manual conf.py can just -# add individual key/value entries. -html_theme_options = { -} +if LooseVersion(sphinx_rtd_theme.__version__) >= LooseVersion("0.4.3"): + html_theme_options = { + "style_nav_header_background": "#802400", + } + +html_logo = os.path.join(qemu_docdir, "../ui/icons/qemu_128x128.png") + +html_favicon = os.path.join(qemu_docdir, "../ui/icons/qemu_32x32.png") # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -# QEMU doesn't yet have any static files, so comment this out so we don't -# get a warning about a missing directory. -# If we do ever add this then it would probably be better to call the -# subdirectory sphinx_static, as the Linux kernel does. -# html_static_path = ['_static'] +html_static_path = [os.path.join(qemu_docdir, "sphinx-static")] + +html_css_files = [ + 'theme_overrides.css', +] + +html_context = { + "display_gitlab": True, + "gitlab_user": "qemu-project", + "gitlab_repo": "qemu", + "gitlab_version": "master", + "conf_py_path": "/docs/", # Path in the checkout to the docs root +} # Custom sidebar templates, must be a dictionary that maps document names # to template names. -# -# This is required for the alabaster theme -# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -html_sidebars = { - '**': [ - 'about.html', - 'editpage.html', - 'navigation.html', - 'searchbox.html', - ] -} +#html_sidebars = {} # Don't copy the rST source files to the HTML output directory, # and don't put links to the sources into the output HTML. diff --git a/docs/devel/_templates/editpage.html b/docs/devel/_templates/editpage.html deleted file mode 100644 index a86d22bca8..0000000000 --- a/docs/devel/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/devel/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/docs/devel/qgraph.rst b/docs/devel/qgraph.rst index a9aff167ad..318534d4b0 100644 --- a/docs/devel/qgraph.rst +++ b/docs/devel/qgraph.rst @@ -92,6 +92,64 @@ The basic framework steps are the following: Depending on the QEMU binary used, only some drivers/machines will be available and only test that are reached by them will be executed. +Troubleshooting unavailable tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If there is no path from an available machine to a test then that test will be +unavailable and won't execute. This can happen if a test or driver did not set +up its qgraph node correctly. It can also happen if the necessary machine type +or device is missing from the QEMU binary because it was compiled out or +otherwise. + +It is possible to troubleshoot unavailable tests by running:: + + $ QTEST_QEMU_BINARY=build/qemu-system-x86_64 build/tests/qtest/qos-test --verbose + # ALL QGRAPH EDGES: { + # src='virtio-net' + # |-> dest='virtio-net-tests/vhost-user/multiqueue' type=2 (node=0x559142109e30) + # |-> dest='virtio-net-tests/vhost-user/migrate' type=2 (node=0x559142109d00) + # src='virtio-net-pci' + # |-> dest='virtio-net' type=1 (node=0x55914210d740) + # src='pci-bus' + # |-> dest='virtio-net-pci' type=2 (node=0x55914210d880) + # src='pci-bus-pc' + # |-> dest='pci-bus' type=1 (node=0x559142103f40) + # src='i440FX-pcihost' + # |-> dest='pci-bus-pc' type=0 (node=0x55914210ac70) + # src='x86_64/pc' + # |-> dest='i440FX-pcihost' type=0 (node=0x5591421117f0) + # src='' + # |-> dest='x86_64/pc' type=0 (node=0x559142111600) + # |-> dest='arm/raspi2' type=0 (node=0x559142110740) + ... + # } + # ALL QGRAPH NODES: { + # name='virtio-net-tests/announce-self' type=3 cmd_line='(null)' [available] + # name='arm/raspi2' type=0 cmd_line='-M raspi2 ' [UNAVAILABLE] + ... + # } + +The ``virtio-net-tests/announce-self`` test is listed as "available" in the +"ALL QGRAPH NODES" output. This means the test will execute. We can follow the +qgraph path in the "ALL QGRAPH EDGES" output as follows: '' -> 'x86_64/pc' -> +'i440FX-pcihost' -> 'pci-bus-pc' -> 'pci-bus' -> 'virtio-net-pci' -> +'virtio-net'. The root of the qgraph is '' and the depth first search begins +there. + +The ``arm/raspi`` machine node is listed as "UNAVAILABLE". Although it is +reachable from the root via '' -> 'arm/raspi2' the node is unavailable because +the QEMU binary did not list it when queried by the framework. This is expected +because we used the ``qemu-system-x86_64`` binary which does not support ARM +machine types. + +If a test is unexpectedly listed as "UNAVAILABLE", first check that the "ALL +QGRAPH EDGES" output reports edge connectivity from the root ('') to the test. +If there is no connectivity then the qgraph nodes were not set up correctly and +the driver or test code is incorrect. If there is connectivity, check the +availability of each node in the path in the "ALL QGRAPH NODES" output. The +first unavailable node in the path is the reason why the test is unavailable. +Typically this is because the QEMU binary lacks support for the necessary +machine type or device. + Creating a new driver and its interface """"""""""""""""""""""""""""""""""""""""" diff --git a/docs/interop/_templates/editpage.html b/docs/interop/_templates/editpage.html deleted file mode 100644 index 215e562681..0000000000 --- a/docs/interop/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/interop/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/docs/meson.build b/docs/meson.build index f84306ba7e..855e3916e9 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -27,10 +27,9 @@ if sphinx_build.found() build_docs = (sphinx_build_test_out.returncode() == 0) if not build_docs - warning('@0@ is either too old or uses too old a Python version' - .format(sphinx_build.full_path())) + warning('@0@: @1@'.format(sphinx_build.full_path(), sphinx_build_test_out.stderr())) if get_option('docs').enabled() - error('Install a Python 3 version of python-sphinx') + error('Install a Python 3 version of python-sphinx and the readthedoc theme') endif endif endif diff --git a/docs/specs/_templates/editpage.html b/docs/specs/_templates/editpage.html deleted file mode 100644 index aaa468aa98..0000000000 --- a/docs/specs/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/specs/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css new file mode 100644 index 0000000000..c70ef95128 --- /dev/null +++ b/docs/sphinx-static/theme_overrides.css @@ -0,0 +1,161 @@ +/* -*- coding: utf-8; mode: css -*- + * + * Sphinx HTML theme customization: read the doc + * Based on Linux Documentation/sphinx-static/theme_overrides.css + */ + +/* Improve contrast and increase size for easier reading. */ + +body { + font-family: serif; + color: black; + font-size: 100%; +} + +h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend { + font-family: sans-serif; +} + +.rst-content dl:not(.docutils) dt { + border-top: none; + border-left: solid 3px #ccc; + background-color: #f0f0f0; + color: black; +} + +.wy-nav-top { + background: #802400; +} + +.wy-side-nav-search input[type="text"] { + border-color: #f60; +} + +.wy-menu-vertical p.caption { + color: white; +} + +.wy-menu-vertical li.current a { + color: #505050; +} + +.wy-menu-vertical li.on a, .wy-menu-vertical li.current > a { + color: #303030; +} + +.fa-gitlab { + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); + border-radius: 5px; +} + +div[class^="highlight"] pre { + font-family: monospace; + color: black; + font-size: 100%; +} + +.wy-menu-vertical { + font-family: sans-serif; +} + +.c { + font-style: normal; +} + +p { + font-size: 100%; +} + +/* Interim: Code-blocks with line nos - lines and line numbers don't line up. + * see: https://github.com/rtfd/sphinx_rtd_theme/issues/419 + */ + +div[class^="highlight"] pre { + line-height: normal; +} +.rst-content .highlight > pre { + line-height: normal; +} + +/* Keep fields from being strangely far apart due to inheirited table CSS. */ +.rst-content table.field-list th.field-name { + padding-top: 1px; + padding-bottom: 1px; +} +.rst-content table.field-list td.field-body { + padding-top: 1px; + padding-bottom: 1px; +} + +@media screen { + + /* content column + * + * RTD theme's default is 800px as max width for the content, but we have + * tables with tons of columns, which need the full width of the view-port. + */ + + .wy-nav-content{max-width: none; } + + /* table: + * + * - Sequences of whitespace should collapse into a single whitespace. + * - make the overflow auto (scrollbar if needed) + * - align caption "left" ("center" is unsuitable on vast tables) + */ + + .wy-table-responsive table td { white-space: normal; } + .wy-table-responsive { overflow: auto; } + .rst-content table.docutils caption { text-align: left; font-size: 100%; } + + /* captions: + * + * - captions should have 100% (not 85%) font size + * - hide the permalink symbol as long as link is not hovered + */ + + .toc-title { + font-size: 150%; + font-weight: bold; + } + + caption, .wy-table caption, .rst-content table.field-list caption { + font-size: 100%; + } + caption a.headerlink { opacity: 0; } + caption a.headerlink:hover { opacity: 1; } + + /* Menu selection and keystrokes */ + + span.menuselection { + color: blue; + font-family: "Courier New", Courier, monospace + } + + code.kbd, code.kbd span { + color: white; + background-color: darkblue; + font-weight: bold; + font-family: "Courier New", Courier, monospace + } + + /* fix bottom margin of lists items */ + + .rst-content .section ul li:last-child, .rst-content .section ul li p:last-child { + margin-bottom: 12px; + } + + /* inline literal: drop the borderbox, padding and red color */ + + code, .rst-content tt, .rst-content code { + color: inherit; + border: none; + padding: unset; + background: inherit; + font-size: 85%; + } + + .rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal { + color: inherit; + } +} diff --git a/docs/system/_templates/editpage.html b/docs/system/_templates/editpage.html deleted file mode 100644 index 6586b2e257..0000000000 --- a/docs/system/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/system/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/docs/tools/_templates/editpage.html b/docs/tools/_templates/editpage.html deleted file mode 100644 index 2a9c8fc92b..0000000000 --- a/docs/tools/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/tools/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index c9efcfaefc..cfe1147879 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -866,6 +866,37 @@ Supported image file formats: issue ``lsattr filename`` to check if the NOCOW flag is set or not (Capital 'C' is NOCOW flag). + ``data_file`` + Filename where all guest data will be stored. If this option is used, + the qcow2 file will only contain the image's metadata. + + Note: Data loss will occur if the given filename already exists when + using this option with ``qemu-img create`` since ``qemu-img`` will create + the data file anew, overwriting the file's original contents. To simply + update the reference to point to the given pre-existing file, use + ``qemu-img amend``. + + ``data_file_raw`` + If this option is set to ``on``, QEMU will always keep the external data + file consistent as a standalone read-only raw image. + + It does this by forwarding all write accesses to the qcow2 file through to + the raw data file, including their offsets. Therefore, data that is visible + on the qcow2 node (i.e., to the guest) at some offset is visible at the same + offset in the raw data file. This results in a read-only raw image. Writes + that bypass the qcow2 metadata may corrupt the qcow2 metadata because the + out-of-band writes may result in the metadata falling out of sync with the + raw image. + + If this option is ``off``, QEMU will use the data file to store data in an + arbitrary manner. The file’s content will not make sense without the + accompanying qcow2 metadata. Where data is written will have no relation to + its offset as seen by the guest, and some writes (specifically zero writes) + may not be forwarded to the data file at all, but will only be handled by + modifying qcow2 metadata. + + This option can only be enabled if ``data_file`` is set. + ``Other`` QEMU also supports various other image file formats for diff --git a/docs/user/_templates/editpage.html b/docs/user/_templates/editpage.html deleted file mode 100644 index 1f5ee01e60..0000000000 --- a/docs/user/_templates/editpage.html +++ /dev/null @@ -1,5 +0,0 @@ -<div id="editpage"> - <ul> - <li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/user/{{pagename}}.rst">Page source</a></li> - </ul> -</div> diff --git a/fpu/softfloat-parts-addsub.c.inc b/fpu/softfloat-parts-addsub.c.inc new file mode 100644 index 0000000000..ae5c1017c5 --- /dev/null +++ b/fpu/softfloat-parts-addsub.c.inc @@ -0,0 +1,62 @@ +/* + * Floating point arithmetic implementation + * + * The code in this source file is derived from release 2a of the SoftFloat + * IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and + * some later contributions) are provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file after December 1st 2014 will be + * taken to be licensed under the Softfloat-2a license unless specifically + * indicated otherwise. + */ + +static void partsN(add_normal)(FloatPartsN *a, FloatPartsN *b) +{ + int exp_diff = a->exp - b->exp; + + if (exp_diff > 0) { + frac_shrjam(b, exp_diff); + } else if (exp_diff < 0) { + frac_shrjam(a, -exp_diff); + a->exp = b->exp; + } + + if (frac_add(a, a, b)) { + frac_shrjam(a, 1); + a->frac_hi |= DECOMPOSED_IMPLICIT_BIT; + a->exp += 1; + } +} + +static bool partsN(sub_normal)(FloatPartsN *a, FloatPartsN *b) +{ + int exp_diff = a->exp - b->exp; + int shift; + + if (exp_diff > 0) { + frac_shrjam(b, exp_diff); + frac_sub(a, a, b); + } else if (exp_diff < 0) { + a->exp = b->exp; + a->sign ^= 1; + frac_shrjam(a, -exp_diff); + frac_sub(a, b, a); + } else if (frac_sub(a, a, b)) { + /* Overflow means that A was less than B. */ + frac_neg(a); + a->sign ^= 1; + } + + shift = frac_normalize(a); + if (likely(shift < N)) { + a->exp -= shift; + return true; + } + a->cls = float_class_zero; + return false; +} diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc new file mode 100644 index 0000000000..a897a5a743 --- /dev/null +++ b/fpu/softfloat-parts.c.inc @@ -0,0 +1,817 @@ +/* + * QEMU float support + * + * The code in this source file is derived from release 2a of the SoftFloat + * IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and + * some later contributions) are provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file after December 1st 2014 will be + * taken to be licensed under the Softfloat-2a license unless specifically + * indicated otherwise. + */ + +static void partsN(return_nan)(FloatPartsN *a, float_status *s) +{ + switch (a->cls) { + case float_class_snan: + float_raise(float_flag_invalid, s); + if (s->default_nan_mode) { + parts_default_nan(a, s); + } else { + parts_silence_nan(a, s); + } + break; + case float_class_qnan: + if (s->default_nan_mode) { + parts_default_nan(a, s); + } + break; + default: + g_assert_not_reached(); + } +} + +static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, + float_status *s) +{ + if (is_snan(a->cls) || is_snan(b->cls)) { + float_raise(float_flag_invalid, s); + } + + if (s->default_nan_mode) { + parts_default_nan(a, s); + } else { + int cmp = frac_cmp(a, b); + if (cmp == 0) { + cmp = a->sign < b->sign; + } + + if (pickNaN(a->cls, b->cls, cmp > 0, s)) { + a = b; + } + if (is_snan(a->cls)) { + parts_silence_nan(a, s); + } + } + return a; +} + +static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, + FloatPartsN *c, float_status *s, + int ab_mask, int abc_mask) +{ + int which; + + if (unlikely(abc_mask & float_cmask_snan)) { + float_raise(float_flag_invalid, s); + } + + which = pickNaNMulAdd(a->cls, b->cls, c->cls, + ab_mask == float_cmask_infzero, s); + + if (s->default_nan_mode || which == 3) { + /* + * Note that this check is after pickNaNMulAdd so that function + * has an opportunity to set the Invalid flag for infzero. + */ + parts_default_nan(a, s); + return a; + } + + switch (which) { + case 0: + break; + case 1: + a = b; + break; + case 2: + a = c; + break; + default: + g_assert_not_reached(); + } + if (is_snan(a->cls)) { + parts_silence_nan(a, s); + } + return a; +} + +/* + * Canonicalize the FloatParts structure. Determine the class, + * unbias the exponent, and normalize the fraction. + */ +static void partsN(canonicalize)(FloatPartsN *p, float_status *status, + const FloatFmt *fmt) +{ + if (unlikely(p->exp == 0)) { + if (likely(frac_eqz(p))) { + p->cls = float_class_zero; + } else if (status->flush_inputs_to_zero) { + float_raise(float_flag_input_denormal, status); + p->cls = float_class_zero; + frac_clear(p); + } else { + int shift = frac_normalize(p); + p->cls = float_class_normal; + p->exp = fmt->frac_shift - fmt->exp_bias - shift + 1; + } + } else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) { + p->cls = float_class_normal; + p->exp -= fmt->exp_bias; + frac_shl(p, fmt->frac_shift); + p->frac_hi |= DECOMPOSED_IMPLICIT_BIT; + } else if (likely(frac_eqz(p))) { + p->cls = float_class_inf; + } else { + frac_shl(p, fmt->frac_shift); + p->cls = (parts_is_snan_frac(p->frac_hi, status) + ? float_class_snan : float_class_qnan); + } +} + +/* + * Round and uncanonicalize a floating-point number by parts. There + * are FRAC_SHIFT bits that may require rounding at the bottom of the + * fraction; these bits will be removed. The exponent will be biased + * by EXP_BIAS and must be bounded by [EXP_MAX-1, 0]. + */ +static void partsN(uncanon)(FloatPartsN *p, float_status *s, + const FloatFmt *fmt) +{ + const int exp_max = fmt->exp_max; + const int frac_shift = fmt->frac_shift; + const uint64_t frac_lsb = fmt->frac_lsb; + const uint64_t frac_lsbm1 = fmt->frac_lsbm1; + const uint64_t round_mask = fmt->round_mask; + const uint64_t roundeven_mask = fmt->roundeven_mask; + uint64_t inc; + bool overflow_norm; + int exp, flags = 0; + + if (unlikely(p->cls != float_class_normal)) { + switch (p->cls) { + case float_class_zero: + p->exp = 0; + frac_clear(p); + return; + case float_class_inf: + g_assert(!fmt->arm_althp); + p->exp = fmt->exp_max; + frac_clear(p); + return; + case float_class_qnan: + case float_class_snan: + g_assert(!fmt->arm_althp); + p->exp = fmt->exp_max; + frac_shr(p, fmt->frac_shift); + return; + default: + break; + } + g_assert_not_reached(); + } + + switch (s->float_rounding_mode) { + case float_round_nearest_even: + overflow_norm = false; + inc = ((p->frac_lo & roundeven_mask) != frac_lsbm1 ? frac_lsbm1 : 0); + break; + case float_round_ties_away: + overflow_norm = false; + inc = frac_lsbm1; + break; + case float_round_to_zero: + overflow_norm = true; + inc = 0; + break; + case float_round_up: + inc = p->sign ? 0 : round_mask; + overflow_norm = p->sign; + break; + case float_round_down: + inc = p->sign ? round_mask : 0; + overflow_norm = !p->sign; + break; + case float_round_to_odd: + overflow_norm = true; + inc = p->frac_lo & frac_lsb ? 0 : round_mask; + break; + default: + g_assert_not_reached(); + } + + exp = p->exp + fmt->exp_bias; + if (likely(exp > 0)) { + if (p->frac_lo & round_mask) { + flags |= float_flag_inexact; + if (frac_addi(p, p, inc)) { + frac_shr(p, 1); + p->frac_hi |= DECOMPOSED_IMPLICIT_BIT; + exp++; + } + } + frac_shr(p, frac_shift); + + if (fmt->arm_althp) { + /* ARM Alt HP eschews Inf and NaN for a wider exponent. */ + if (unlikely(exp > exp_max)) { + /* Overflow. Return the maximum normal. */ + flags = float_flag_invalid; + exp = exp_max; + frac_allones(p); + } + } else if (unlikely(exp >= exp_max)) { + flags |= float_flag_overflow | float_flag_inexact; + if (overflow_norm) { + exp = exp_max - 1; + frac_allones(p); + } else { + p->cls = float_class_inf; + exp = exp_max; + frac_clear(p); + } + } + } else if (s->flush_to_zero) { + flags |= float_flag_output_denormal; + p->cls = float_class_zero; + exp = 0; + frac_clear(p); + } else { + bool is_tiny = s->tininess_before_rounding || exp < 0; + + if (!is_tiny) { + FloatPartsN discard; + is_tiny = !frac_addi(&discard, p, inc); + } + + frac_shrjam(p, 1 - exp); + + if (p->frac_lo & round_mask) { + /* Need to recompute round-to-even/round-to-odd. */ + switch (s->float_rounding_mode) { + case float_round_nearest_even: + inc = ((p->frac_lo & roundeven_mask) != frac_lsbm1 + ? frac_lsbm1 : 0); + break; + case float_round_to_odd: + inc = p->frac_lo & frac_lsb ? 0 : round_mask; + break; + default: + break; + } + flags |= float_flag_inexact; + frac_addi(p, p, inc); + } + + exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) != 0; + frac_shr(p, frac_shift); + + if (is_tiny && (flags & float_flag_inexact)) { + flags |= float_flag_underflow; + } + if (exp == 0 && frac_eqz(p)) { + p->cls = float_class_zero; + } + } + p->exp = exp; + float_raise(flags, s); +} + +/* + * Returns the result of adding or subtracting the values of the + * floating-point values `a' and `b'. The operation is performed + * according to the IEC/IEEE Standard for Binary Floating-Point + * Arithmetic. + */ +static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, + float_status *s, bool subtract) +{ + bool b_sign = b->sign ^ subtract; + int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); + + if (a->sign != b_sign) { + /* Subtraction */ + if (likely(ab_mask == float_cmask_normal)) { + if (parts_sub_normal(a, b)) { + return a; + } + /* Subtract was exact, fall through to set sign. */ + ab_mask = float_cmask_zero; + } + + if (ab_mask == float_cmask_zero) { + a->sign = s->float_rounding_mode == float_round_down; + return a; + } + + if (unlikely(ab_mask & float_cmask_anynan)) { + goto p_nan; + } + + if (ab_mask & float_cmask_inf) { + if (a->cls != float_class_inf) { + /* N - Inf */ + goto return_b; + } + if (b->cls != float_class_inf) { + /* Inf - N */ + return a; + } + /* Inf - Inf */ + float_raise(float_flag_invalid, s); + parts_default_nan(a, s); + return a; + } + } else { + /* Addition */ + if (likely(ab_mask == float_cmask_normal)) { + parts_add_normal(a, b); + return a; + } + + if (ab_mask == float_cmask_zero) { + return a; + } + + if (unlikely(ab_mask & float_cmask_anynan)) { + goto p_nan; + } + + if (ab_mask & float_cmask_inf) { + a->cls = float_class_inf; + return a; + } + } + + if (b->cls == float_class_zero) { + g_assert(a->cls == float_class_normal); + return a; + } + + g_assert(a->cls == float_class_zero); + g_assert(b->cls == float_class_normal); + return_b: + b->sign = b_sign; + return b; + + p_nan: + return parts_pick_nan(a, b, s); +} + +/* + * Returns the result of multiplying the floating-point values `a' and + * `b'. The operation is performed according to the IEC/IEEE Standard + * for Binary Floating-Point Arithmetic. + */ +static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, + float_status *s) +{ + int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); + bool sign = a->sign ^ b->sign; + + if (likely(ab_mask == float_cmask_normal)) { + FloatPartsW tmp; + + frac_mulw(&tmp, a, b); + frac_truncjam(a, &tmp); + + a->exp += b->exp + 1; + if (!(a->frac_hi & DECOMPOSED_IMPLICIT_BIT)) { + frac_add(a, a, a); + a->exp -= 1; + } + + a->sign = sign; + return a; + } + + /* Inf * Zero == NaN */ + if (unlikely(ab_mask == float_cmask_infzero)) { + float_raise(float_flag_invalid, s); + parts_default_nan(a, s); + return a; + } + + if (unlikely(ab_mask & float_cmask_anynan)) { + return parts_pick_nan(a, b, s); + } + + /* Multiply by 0 or Inf */ + if (ab_mask & float_cmask_inf) { + a->cls = float_class_inf; + a->sign = sign; + return a; + } + + g_assert(ab_mask & float_cmask_zero); + a->cls = float_class_zero; + a->sign = sign; + return a; +} + +/* + * Returns the result of multiplying the floating-point values `a' and + * `b' then adding 'c', with no intermediate rounding step after the + * multiplication. The operation is performed according to the + * IEC/IEEE Standard for Binary Floating-Point Arithmetic 754-2008. + * The flags argument allows the caller to select negation of the + * addend, the intermediate product, or the final result. (The + * difference between this and having the caller do a separate + * negation is that negating externally will flip the sign bit on NaNs.) + * + * Requires A and C extracted into a double-sized structure to provide the + * extra space for the widening multiply. + */ +static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, + FloatPartsN *c, int flags, float_status *s) +{ + int ab_mask, abc_mask; + FloatPartsW p_widen, c_widen; + + ab_mask = float_cmask(a->cls) | float_cmask(b->cls); + abc_mask = float_cmask(c->cls) | ab_mask; + + /* + * It is implementation-defined whether the cases of (0,inf,qnan) + * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN + * they return if they do), so we have to hand this information + * off to the target-specific pick-a-NaN routine. + */ + if (unlikely(abc_mask & float_cmask_anynan)) { + return parts_pick_nan_muladd(a, b, c, s, ab_mask, abc_mask); + } + + if (flags & float_muladd_negate_c) { + c->sign ^= 1; + } + + /* Compute the sign of the product into A. */ + a->sign ^= b->sign; + if (flags & float_muladd_negate_product) { + a->sign ^= 1; + } + + if (unlikely(ab_mask != float_cmask_normal)) { + if (unlikely(ab_mask == float_cmask_infzero)) { + goto d_nan; + } + + if (ab_mask & float_cmask_inf) { + if (c->cls == float_class_inf && a->sign != c->sign) { + goto d_nan; + } + goto return_inf; + } + + g_assert(ab_mask & float_cmask_zero); + if (c->cls == float_class_normal) { + *a = *c; + goto return_normal; + } + if (c->cls == float_class_zero) { + if (a->sign != c->sign) { + goto return_sub_zero; + } + goto return_zero; + } + g_assert(c->cls == float_class_inf); + } + + if (unlikely(c->cls == float_class_inf)) { + a->sign = c->sign; + goto return_inf; + } + + /* Perform the multiplication step. */ + p_widen.sign = a->sign; + p_widen.exp = a->exp + b->exp + 1; + frac_mulw(&p_widen, a, b); + if (!(p_widen.frac_hi & DECOMPOSED_IMPLICIT_BIT)) { + frac_add(&p_widen, &p_widen, &p_widen); + p_widen.exp -= 1; + } + + /* Perform the addition step. */ + if (c->cls != float_class_zero) { + /* Zero-extend C to less significant bits. */ + frac_widen(&c_widen, c); + c_widen.exp = c->exp; + + if (a->sign == c->sign) { + parts_add_normal(&p_widen, &c_widen); + } else if (!parts_sub_normal(&p_widen, &c_widen)) { + goto return_sub_zero; + } + } + + /* Narrow with sticky bit, for proper rounding later. */ + frac_truncjam(a, &p_widen); + a->sign = p_widen.sign; + a->exp = p_widen.exp; + + return_normal: + if (flags & float_muladd_halve_result) { + a->exp -= 1; + } + finish_sign: + if (flags & float_muladd_negate_result) { + a->sign ^= 1; + } + return a; + + return_sub_zero: + a->sign = s->float_rounding_mode == float_round_down; + return_zero: + a->cls = float_class_zero; + goto finish_sign; + + return_inf: + a->cls = float_class_inf; + goto finish_sign; + + d_nan: + float_raise(float_flag_invalid, s); + parts_default_nan(a, s); + return a; +} + +/* + * Returns the result of dividing the floating-point value `a' by the + * corresponding value `b'. The operation is performed according to + * the IEC/IEEE Standard for Binary Floating-Point Arithmetic. + */ +static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, + float_status *s) +{ + int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); + bool sign = a->sign ^ b->sign; + + if (likely(ab_mask == float_cmask_normal)) { + a->sign = sign; + a->exp -= b->exp + frac_div(a, b); + return a; + } + + /* 0/0 or Inf/Inf => NaN */ + if (unlikely(ab_mask == float_cmask_zero) || + unlikely(ab_mask == float_cmask_inf)) { + float_raise(float_flag_invalid, s); + parts_default_nan(a, s); + return a; + } + + /* All the NaN cases */ + if (unlikely(ab_mask & float_cmask_anynan)) { + return parts_pick_nan(a, b, s); + } + + a->sign = sign; + + /* Inf / X */ + if (a->cls == float_class_inf) { + return a; + } + + /* 0 / X */ + if (a->cls == float_class_zero) { + return a; + } + + /* X / Inf */ + if (b->cls == float_class_inf) { + a->cls = float_class_zero; + return a; + } + + /* X / 0 => Inf */ + g_assert(b->cls == float_class_zero); + float_raise(float_flag_divbyzero, s); + a->cls = float_class_inf; + return a; +} + +/* + * Rounds the floating-point value `a' to an integer, and returns the + * result as a floating-point value. The operation is performed + * according to the IEC/IEEE Standard for Binary Floating-Point + * Arithmetic. + * + * parts_round_to_int_normal is an internal helper function for + * normal numbers only, returning true for inexact but not directly + * raising float_flag_inexact. + */ +static bool partsN(round_to_int_normal)(FloatPartsN *a, FloatRoundMode rmode, + int scale, int frac_size) +{ + uint64_t frac_lsb, frac_lsbm1, rnd_even_mask, rnd_mask, inc; + int shift_adj; + + scale = MIN(MAX(scale, -0x10000), 0x10000); + a->exp += scale; + + if (a->exp < 0) { + bool one; + + /* All fractional */ + switch (rmode) { + case float_round_nearest_even: + one = false; + if (a->exp == -1) { + FloatPartsN tmp; + /* Shift left one, discarding DECOMPOSED_IMPLICIT_BIT */ + frac_add(&tmp, a, a); + /* Anything remaining means frac > 0.5. */ + one = !frac_eqz(&tmp); + } + break; + case float_round_ties_away: + one = a->exp == -1; + break; + case float_round_to_zero: + one = false; + break; + case float_round_up: + one = !a->sign; + break; + case float_round_down: + one = a->sign; + break; + case float_round_to_odd: + one = true; + break; + default: + g_assert_not_reached(); + } + + frac_clear(a); + a->exp = 0; + if (one) { + a->frac_hi = DECOMPOSED_IMPLICIT_BIT; + } else { + a->cls = float_class_zero; + } + return true; + } + + if (a->exp >= frac_size) { + /* All integral */ + return false; + } + + if (N > 64 && a->exp < N - 64) { + /* + * Rounding is not in the low word -- shift lsb to bit 2, + * which leaves room for sticky and rounding bit. + */ + shift_adj = (N - 1) - (a->exp + 2); + frac_shrjam(a, shift_adj); + frac_lsb = 1 << 2; + } else { + shift_adj = 0; + frac_lsb = DECOMPOSED_IMPLICIT_BIT >> (a->exp & 63); + } + + frac_lsbm1 = frac_lsb >> 1; + rnd_mask = frac_lsb - 1; + rnd_even_mask = rnd_mask | frac_lsb; + + if (!(a->frac_lo & rnd_mask)) { + /* Fractional bits already clear, undo the shift above. */ + frac_shl(a, shift_adj); + return false; + } + + switch (rmode) { + case float_round_nearest_even: + inc = ((a->frac_lo & rnd_even_mask) != frac_lsbm1 ? frac_lsbm1 : 0); + break; + case float_round_ties_away: + inc = frac_lsbm1; + break; + case float_round_to_zero: + inc = 0; + break; + case float_round_up: + inc = a->sign ? 0 : rnd_mask; + break; + case float_round_down: + inc = a->sign ? rnd_mask : 0; + break; + case float_round_to_odd: + inc = a->frac_lo & frac_lsb ? 0 : rnd_mask; + break; + default: + g_assert_not_reached(); + } + + if (shift_adj == 0) { + if (frac_addi(a, a, inc)) { + frac_shr(a, 1); + a->frac_hi |= DECOMPOSED_IMPLICIT_BIT; + a->exp++; + } + a->frac_lo &= ~rnd_mask; + } else { + frac_addi(a, a, inc); + a->frac_lo &= ~rnd_mask; + /* Be careful shifting back, not to overflow */ + frac_shl(a, shift_adj - 1); + if (a->frac_hi & DECOMPOSED_IMPLICIT_BIT) { + a->exp++; + } else { + frac_add(a, a, a); + } + } + return true; +} + +static void partsN(round_to_int)(FloatPartsN *a, FloatRoundMode rmode, + int scale, float_status *s, + const FloatFmt *fmt) +{ + switch (a->cls) { + case float_class_qnan: + case float_class_snan: + parts_return_nan(a, s); + break; + case float_class_zero: + case float_class_inf: + break; + case float_class_normal: + if (parts_round_to_int_normal(a, rmode, scale, fmt->frac_size)) { + float_raise(float_flag_inexact, s); + } + break; + default: + g_assert_not_reached(); + } +} + +/* + * Returns the result of converting the floating-point value `a' to + * the two's complement integer format. The conversion is performed + * according to the IEC/IEEE Standard for Binary Floating-Point + * Arithmetic---which means in particular that the conversion is + * rounded according to the current rounding mode. If `a' is a NaN, + * the largest positive integer is returned. Otherwise, if the + * conversion overflows, the largest integer with the same sign as `a' + * is returned. +*/ +static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode, + int scale, int64_t min, int64_t max, + float_status *s) +{ + int flags = 0; + uint64_t r; + + switch (p->cls) { + case float_class_snan: + case float_class_qnan: + flags = float_flag_invalid; + r = max; + break; + + case float_class_inf: + flags = float_flag_invalid; + r = p->sign ? min : max; + break; + + case float_class_zero: + return 0; + + case float_class_normal: + /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ + if (parts_round_to_int_normal(p, rmode, scale, N - 2)) { + flags = float_flag_inexact; + } + + if (p->exp <= DECOMPOSED_BINARY_POINT) { + r = p->frac_hi >> (DECOMPOSED_BINARY_POINT - p->exp); + } else { + r = UINT64_MAX; + } + if (p->sign) { + if (r <= -(uint64_t)min) { + r = -r; + } else { + flags = float_flag_invalid; + r = min; + } + } else if (r > max) { + flags = float_flag_invalid; + r = max; + } + break; + + default: + g_assert_not_reached(); + } + + float_raise(flags, s); + return r; +} diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index e19809c04b..c895733e79 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -129,7 +129,7 @@ static bool parts_is_snan_frac(uint64_t frac, float_status *status) | The pattern for a default generated deconstructed floating-point NaN. *----------------------------------------------------------------------------*/ -static FloatParts parts_default_nan(float_status *status) +static void parts64_default_nan(FloatParts64 *p, float_status *status) { bool sign = 0; uint64_t frac; @@ -163,7 +163,7 @@ static FloatParts parts_default_nan(float_status *status) } #endif - return (FloatParts) { + *p = (FloatParts64) { .cls = float_class_qnan, .sign = sign, .exp = INT_MAX, @@ -171,26 +171,55 @@ static FloatParts parts_default_nan(float_status *status) }; } +static void parts128_default_nan(FloatParts128 *p, float_status *status) +{ + /* + * Extrapolate from the choices made by parts64_default_nan to fill + * in the quad-floating format. If the low bit is set, assume we + * want to set all non-snan bits. + */ + FloatParts64 p64; + parts64_default_nan(&p64, status); + + *p = (FloatParts128) { + .cls = float_class_qnan, + .sign = p64.sign, + .exp = INT_MAX, + .frac_hi = p64.frac, + .frac_lo = -(p64.frac & 1) + }; +} + /*---------------------------------------------------------------------------- | Returns a quiet NaN from a signalling NaN for the deconstructed | floating-point parts. *----------------------------------------------------------------------------*/ -static FloatParts parts_silence_nan(FloatParts a, float_status *status) +static uint64_t parts_silence_nan_frac(uint64_t frac, float_status *status) { g_assert(!no_signaling_nans(status)); -#if defined(TARGET_HPPA) - a.frac &= ~(1ULL << (DECOMPOSED_BINARY_POINT - 1)); - a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 2); -#else + g_assert(!status->default_nan_mode); + + /* The only snan_bit_is_one target without default_nan_mode is HPPA. */ if (snan_bit_is_one(status)) { - return parts_default_nan(status); + frac &= ~(1ULL << (DECOMPOSED_BINARY_POINT - 1)); + frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 2); } else { - a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 1); + frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 1); } -#endif - a.cls = float_class_qnan; - return a; + return frac; +} + +static void parts64_silence_nan(FloatParts64 *p, float_status *status) +{ + p->frac = parts_silence_nan_frac(p->frac, status); + p->cls = float_class_qnan; +} + +static void parts128_silence_nan(FloatParts128 *p, float_status *status) +{ + p->frac_hi = parts_silence_nan_frac(p->frac_hi, status); + p->cls = float_class_qnan; } /*---------------------------------------------------------------------------- @@ -228,18 +257,6 @@ const floatx80 floatx80_infinity = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low); /*---------------------------------------------------------------------------- -| Raises the exceptions specified by `flags'. Floating-point traps can be -| defined here if desired. It is currently not possible for such a trap -| to substitute a result value. If traps are not implemented, this routine -| should be simply `float_exception_flags |= flags;'. -*----------------------------------------------------------------------------*/ - -void float_raise(uint8_t flags, float_status *status) -{ - status->float_exception_flags |= flags; -} - -/*---------------------------------------------------------------------------- | Internal canonical NaN format. *----------------------------------------------------------------------------*/ typedef struct { @@ -1071,25 +1088,6 @@ bool float128_is_signaling_nan(float128 a, float_status *status) } /*---------------------------------------------------------------------------- -| Returns a quiet NaN from a signalling NaN for the quadruple-precision -| floating point value `a'. -*----------------------------------------------------------------------------*/ - -float128 float128_silence_nan(float128 a, float_status *status) -{ - if (no_signaling_nans(status)) { - g_assert_not_reached(); - } else { - if (snan_bit_is_one(status)) { - return float128_default_nan(status); - } else { - a.high |= UINT64_C(0x0000800000000000); - return a; - } - } -} - -/*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 67cfa0fd82..0dc2203477 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -132,7 +132,7 @@ this code that are retained. if (unlikely(soft_t ## _is_denormal(*a))) { \ *a = soft_t ## _set_sign(soft_t ## _zero, \ soft_t ## _is_neg(*a)); \ - s->float_exception_flags |= float_flag_input_denormal; \ + float_raise(float_flag_input_denormal, s); \ } \ } @@ -360,7 +360,7 @@ float32_gen2(float32 xa, float32 xb, float_status *s, ur.h = hard(ua.h, ub.h); if (unlikely(f32_is_inf(ur))) { - s->float_exception_flags |= float_flag_overflow; + float_raise(float_flag_overflow, s); } else if (unlikely(fabsf(ur.h) <= FLT_MIN) && post(ua, ub)) { goto soft; } @@ -391,7 +391,7 @@ float64_gen2(float64 xa, float64 xb, float_status *s, ur.h = hard(ua.h, ub.h); if (unlikely(f64_is_inf(ur))) { - s->float_exception_flags |= float_flag_overflow; + float_raise(float_flag_overflow, s); } else if (unlikely(fabs(ur.h) <= DBL_MIN) && post(ua, ub)) { goto soft; } @@ -469,6 +469,20 @@ typedef enum __attribute__ ((__packed__)) { float_class_snan, } FloatClass; +#define float_cmask(bit) (1u << (bit)) + +enum { + float_cmask_zero = float_cmask(float_class_zero), + float_cmask_normal = float_cmask(float_class_normal), + float_cmask_inf = float_cmask(float_class_inf), + float_cmask_qnan = float_cmask(float_class_qnan), + float_cmask_snan = float_cmask(float_class_snan), + + float_cmask_infzero = float_cmask_zero | float_cmask_inf, + float_cmask_anynan = float_cmask_qnan | float_cmask_snan, +}; + + /* Simple helpers for checking if, or what kind of, NaN we have */ static inline __attribute__((unused)) bool is_nan(FloatClass c) { @@ -486,26 +500,52 @@ static inline __attribute__((unused)) bool is_qnan(FloatClass c) } /* - * Structure holding all of the decomposed parts of a float. The - * exponent is unbiased and the fraction is normalized. All - * calculations are done with a 64 bit fraction and then rounded as - * appropriate for the final format. + * Structure holding all of the decomposed parts of a float. + * The exponent is unbiased and the fraction is normalized. * - * Thanks to the packed FloatClass a decent compiler should be able to - * fit the whole structure into registers and avoid using the stack - * for parameter passing. + * The fraction words are stored in big-endian word ordering, + * so that truncation from a larger format to a smaller format + * can be done simply by ignoring subsequent elements. */ typedef struct { - uint64_t frac; - int32_t exp; FloatClass cls; bool sign; -} FloatParts; + int32_t exp; + union { + /* Routines that know the structure may reference the singular name. */ + uint64_t frac; + /* + * Routines expanded with multiple structures reference "hi" and "lo" + * depending on the operation. In FloatParts64, "hi" and "lo" are + * both the same word and aliased here. + */ + uint64_t frac_hi; + uint64_t frac_lo; + }; +} FloatParts64; -#define DECOMPOSED_BINARY_POINT (64 - 2) +typedef struct { + FloatClass cls; + bool sign; + int32_t exp; + uint64_t frac_hi; + uint64_t frac_lo; +} FloatParts128; + +typedef struct { + FloatClass cls; + bool sign; + int32_t exp; + uint64_t frac_hi; + uint64_t frac_hm; /* high-middle */ + uint64_t frac_lm; /* low-middle */ + uint64_t frac_lo; +} FloatParts256; + +/* These apply to the most significant word of each FloatPartsN. */ +#define DECOMPOSED_BINARY_POINT 63 #define DECOMPOSED_IMPLICIT_BIT (1ull << DECOMPOSED_BINARY_POINT) -#define DECOMPOSED_OVERFLOW_BIT (DECOMPOSED_IMPLICIT_BIT << 1) /* Structure holding all of the relevant parameters for a format. * exp_size: the size of the exponent field @@ -539,11 +579,11 @@ typedef struct { .exp_bias = ((1 << E) - 1) >> 1, \ .exp_max = (1 << E) - 1, \ .frac_size = F, \ - .frac_shift = DECOMPOSED_BINARY_POINT - F, \ - .frac_lsb = 1ull << (DECOMPOSED_BINARY_POINT - F), \ - .frac_lsbm1 = 1ull << ((DECOMPOSED_BINARY_POINT - F) - 1), \ - .round_mask = (1ull << (DECOMPOSED_BINARY_POINT - F)) - 1, \ - .roundeven_mask = (2ull << (DECOMPOSED_BINARY_POINT - F)) - 1 + .frac_shift = (-F - 1) & 63, \ + .frac_lsb = 1ull << ((-F - 1) & 63), \ + .frac_lsbm1 = 1ull << ((-F - 2) & 63), \ + .round_mask = (1ull << ((-F - 1) & 63)) - 1, \ + .roundeven_mask = (2ull << ((-F - 1) & 63)) - 1 static const FloatFmt float16_params = { FLOAT_PARAMS(5, 10) @@ -566,65 +606,101 @@ static const FloatFmt float64_params = { FLOAT_PARAMS(11, 52) }; +static const FloatFmt float128_params = { + FLOAT_PARAMS(15, 112) +}; + /* Unpack a float to parts, but do not canonicalize. */ -static inline FloatParts unpack_raw(FloatFmt fmt, uint64_t raw) +static void unpack_raw64(FloatParts64 *r, const FloatFmt *fmt, uint64_t raw) { - const int sign_pos = fmt.frac_size + fmt.exp_size; + const int f_size = fmt->frac_size; + const int e_size = fmt->exp_size; - return (FloatParts) { + *r = (FloatParts64) { .cls = float_class_unclassified, - .sign = extract64(raw, sign_pos, 1), - .exp = extract64(raw, fmt.frac_size, fmt.exp_size), - .frac = extract64(raw, 0, fmt.frac_size), + .sign = extract64(raw, f_size + e_size, 1), + .exp = extract64(raw, f_size, e_size), + .frac = extract64(raw, 0, f_size) }; } -static inline FloatParts float16_unpack_raw(float16 f) +static inline void float16_unpack_raw(FloatParts64 *p, float16 f) { - return unpack_raw(float16_params, f); + unpack_raw64(p, &float16_params, f); } -static inline FloatParts bfloat16_unpack_raw(bfloat16 f) +static inline void bfloat16_unpack_raw(FloatParts64 *p, bfloat16 f) { - return unpack_raw(bfloat16_params, f); + unpack_raw64(p, &bfloat16_params, f); } -static inline FloatParts float32_unpack_raw(float32 f) +static inline void float32_unpack_raw(FloatParts64 *p, float32 f) { - return unpack_raw(float32_params, f); + unpack_raw64(p, &float32_params, f); } -static inline FloatParts float64_unpack_raw(float64 f) +static inline void float64_unpack_raw(FloatParts64 *p, float64 f) { - return unpack_raw(float64_params, f); + unpack_raw64(p, &float64_params, f); +} + +static void float128_unpack_raw(FloatParts128 *p, float128 f) +{ + const int f_size = float128_params.frac_size - 64; + const int e_size = float128_params.exp_size; + + *p = (FloatParts128) { + .cls = float_class_unclassified, + .sign = extract64(f.high, f_size + e_size, 1), + .exp = extract64(f.high, f_size, e_size), + .frac_hi = extract64(f.high, 0, f_size), + .frac_lo = f.low, + }; } /* Pack a float from parts, but do not canonicalize. */ -static inline uint64_t pack_raw(FloatFmt fmt, FloatParts p) +static uint64_t pack_raw64(const FloatParts64 *p, const FloatFmt *fmt) +{ + const int f_size = fmt->frac_size; + const int e_size = fmt->exp_size; + uint64_t ret; + + ret = (uint64_t)p->sign << (f_size + e_size); + ret = deposit64(ret, f_size, e_size, p->exp); + ret = deposit64(ret, 0, f_size, p->frac); + return ret; +} + +static inline float16 float16_pack_raw(const FloatParts64 *p) { - const int sign_pos = fmt.frac_size + fmt.exp_size; - uint64_t ret = deposit64(p.frac, fmt.frac_size, fmt.exp_size, p.exp); - return deposit64(ret, sign_pos, 1, p.sign); + return make_float16(pack_raw64(p, &float16_params)); } -static inline float16 float16_pack_raw(FloatParts p) +static inline bfloat16 bfloat16_pack_raw(const FloatParts64 *p) { - return make_float16(pack_raw(float16_params, p)); + return pack_raw64(p, &bfloat16_params); } -static inline bfloat16 bfloat16_pack_raw(FloatParts p) +static inline float32 float32_pack_raw(const FloatParts64 *p) { - return pack_raw(bfloat16_params, p); + return make_float32(pack_raw64(p, &float32_params)); } -static inline float32 float32_pack_raw(FloatParts p) +static inline float64 float64_pack_raw(const FloatParts64 *p) { - return make_float32(pack_raw(float32_params, p)); + return make_float64(pack_raw64(p, &float64_params)); } -static inline float64 float64_pack_raw(FloatParts p) +static float128 float128_pack_raw(const FloatParts128 *p) { - return make_float64(pack_raw(float64_params, p)); + const int f_size = float128_params.frac_size - 64; + const int e_size = float128_params.exp_size; + uint64_t hi; + + hi = (uint64_t)p->sign << (f_size + e_size); + hi = deposit64(hi, f_size, e_size, p->exp); + hi = deposit64(hi, 0, f_size, p->frac_hi); + return make_float128(hi, p->frac_lo); } /*---------------------------------------------------------------------------- @@ -637,474 +713,807 @@ static inline float64 float64_pack_raw(FloatParts p) *----------------------------------------------------------------------------*/ #include "softfloat-specialize.c.inc" -/* Canonicalize EXP and FRAC, setting CLS. */ -static FloatParts sf_canonicalize(FloatParts part, const FloatFmt *parm, - float_status *status) +#define PARTS_GENERIC_64_128(NAME, P) \ + QEMU_GENERIC(P, (FloatParts128 *, parts128_##NAME), parts64_##NAME) + +#define PARTS_GENERIC_64_128_256(NAME, P) \ + QEMU_GENERIC(P, (FloatParts256 *, parts256_##NAME), \ + (FloatParts128 *, parts128_##NAME), parts64_##NAME) + +#define parts_default_nan(P, S) PARTS_GENERIC_64_128(default_nan, P)(P, S) +#define parts_silence_nan(P, S) PARTS_GENERIC_64_128(silence_nan, P)(P, S) + +static void parts64_return_nan(FloatParts64 *a, float_status *s); +static void parts128_return_nan(FloatParts128 *a, float_status *s); + +#define parts_return_nan(P, S) PARTS_GENERIC_64_128(return_nan, P)(P, S) + +static FloatParts64 *parts64_pick_nan(FloatParts64 *a, FloatParts64 *b, + float_status *s); +static FloatParts128 *parts128_pick_nan(FloatParts128 *a, FloatParts128 *b, + float_status *s); + +#define parts_pick_nan(A, B, S) PARTS_GENERIC_64_128(pick_nan, A)(A, B, S) + +static FloatParts64 *parts64_pick_nan_muladd(FloatParts64 *a, FloatParts64 *b, + FloatParts64 *c, float_status *s, + int ab_mask, int abc_mask); +static FloatParts128 *parts128_pick_nan_muladd(FloatParts128 *a, + FloatParts128 *b, + FloatParts128 *c, + float_status *s, + int ab_mask, int abc_mask); + +#define parts_pick_nan_muladd(A, B, C, S, ABM, ABCM) \ + PARTS_GENERIC_64_128(pick_nan_muladd, A)(A, B, C, S, ABM, ABCM) + +static void parts64_canonicalize(FloatParts64 *p, float_status *status, + const FloatFmt *fmt); +static void parts128_canonicalize(FloatParts128 *p, float_status *status, + const FloatFmt *fmt); + +#define parts_canonicalize(A, S, F) \ + PARTS_GENERIC_64_128(canonicalize, A)(A, S, F) + +static void parts64_uncanon(FloatParts64 *p, float_status *status, + const FloatFmt *fmt); +static void parts128_uncanon(FloatParts128 *p, float_status *status, + const FloatFmt *fmt); + +#define parts_uncanon(A, S, F) \ + PARTS_GENERIC_64_128(uncanon, A)(A, S, F) + +static void parts64_add_normal(FloatParts64 *a, FloatParts64 *b); +static void parts128_add_normal(FloatParts128 *a, FloatParts128 *b); +static void parts256_add_normal(FloatParts256 *a, FloatParts256 *b); + +#define parts_add_normal(A, B) \ + PARTS_GENERIC_64_128_256(add_normal, A)(A, B) + +static bool parts64_sub_normal(FloatParts64 *a, FloatParts64 *b); +static bool parts128_sub_normal(FloatParts128 *a, FloatParts128 *b); +static bool parts256_sub_normal(FloatParts256 *a, FloatParts256 *b); + +#define parts_sub_normal(A, B) \ + PARTS_GENERIC_64_128_256(sub_normal, A)(A, B) + +static FloatParts64 *parts64_addsub(FloatParts64 *a, FloatParts64 *b, + float_status *s, bool subtract); +static FloatParts128 *parts128_addsub(FloatParts128 *a, FloatParts128 *b, + float_status *s, bool subtract); + +#define parts_addsub(A, B, S, Z) \ + PARTS_GENERIC_64_128(addsub, A)(A, B, S, Z) + +static FloatParts64 *parts64_mul(FloatParts64 *a, FloatParts64 *b, + float_status *s); +static FloatParts128 *parts128_mul(FloatParts128 *a, FloatParts128 *b, + float_status *s); + +#define parts_mul(A, B, S) \ + PARTS_GENERIC_64_128(mul, A)(A, B, S) + +static FloatParts64 *parts64_muladd(FloatParts64 *a, FloatParts64 *b, + FloatParts64 *c, int flags, + float_status *s); +static FloatParts128 *parts128_muladd(FloatParts128 *a, FloatParts128 *b, + FloatParts128 *c, int flags, + float_status *s); + +#define parts_muladd(A, B, C, Z, S) \ + PARTS_GENERIC_64_128(muladd, A)(A, B, C, Z, S) + +static FloatParts64 *parts64_div(FloatParts64 *a, FloatParts64 *b, + float_status *s); +static FloatParts128 *parts128_div(FloatParts128 *a, FloatParts128 *b, + float_status *s); + +#define parts_div(A, B, S) \ + PARTS_GENERIC_64_128(div, A)(A, B, S) + +static bool parts64_round_to_int_normal(FloatParts64 *a, FloatRoundMode rm, + int scale, int frac_size); +static bool parts128_round_to_int_normal(FloatParts128 *a, FloatRoundMode r, + int scale, int frac_size); + +#define parts_round_to_int_normal(A, R, C, F) \ + PARTS_GENERIC_64_128(round_to_int_normal, A)(A, R, C, F) + +static void parts64_round_to_int(FloatParts64 *a, FloatRoundMode rm, + int scale, float_status *s, + const FloatFmt *fmt); +static void parts128_round_to_int(FloatParts128 *a, FloatRoundMode r, + int scale, float_status *s, + const FloatFmt *fmt); + +#define parts_round_to_int(A, R, C, S, F) \ + PARTS_GENERIC_64_128(round_to_int, A)(A, R, C, S, F) + +static int64_t parts64_float_to_sint(FloatParts64 *p, FloatRoundMode rmode, + int scale, int64_t min, int64_t max, + float_status *s); +static int64_t parts128_float_to_sint(FloatParts128 *p, FloatRoundMode rmode, + int scale, int64_t min, int64_t max, + float_status *s); + +#define parts_float_to_sint(P, R, Z, MN, MX, S) \ + PARTS_GENERIC_64_128(float_to_sint, P)(P, R, Z, MN, MX, S) + +/* + * Helper functions for softfloat-parts.c.inc, per-size operations. + */ + +#define FRAC_GENERIC_64_128(NAME, P) \ + QEMU_GENERIC(P, (FloatParts128 *, frac128_##NAME), frac64_##NAME) + +#define FRAC_GENERIC_64_128_256(NAME, P) \ + QEMU_GENERIC(P, (FloatParts256 *, frac256_##NAME), \ + (FloatParts128 *, frac128_##NAME), frac64_##NAME) + +static bool frac64_add(FloatParts64 *r, FloatParts64 *a, FloatParts64 *b) { - if (part.exp == parm->exp_max && !parm->arm_althp) { - if (part.frac == 0) { - part.cls = float_class_inf; - } else { - part.frac <<= parm->frac_shift; - part.cls = (parts_is_snan_frac(part.frac, status) - ? float_class_snan : float_class_qnan); - } - } else if (part.exp == 0) { - if (likely(part.frac == 0)) { - part.cls = float_class_zero; - } else if (status->flush_inputs_to_zero) { - float_raise(float_flag_input_denormal, status); - part.cls = float_class_zero; - part.frac = 0; - } else { - int shift = clz64(part.frac) - 1; - part.cls = float_class_normal; - part.exp = parm->frac_shift - parm->exp_bias - shift + 1; - part.frac <<= shift; - } - } else { - part.cls = float_class_normal; - part.exp -= parm->exp_bias; - part.frac = DECOMPOSED_IMPLICIT_BIT + (part.frac << parm->frac_shift); - } - return part; + return uadd64_overflow(a->frac, b->frac, &r->frac); } -/* Round and uncanonicalize a floating-point number by parts. There - * are FRAC_SHIFT bits that may require rounding at the bottom of the - * fraction; these bits will be removed. The exponent will be biased - * by EXP_BIAS and must be bounded by [EXP_MAX-1, 0]. - */ +static bool frac128_add(FloatParts128 *r, FloatParts128 *a, FloatParts128 *b) +{ + bool c = 0; + r->frac_lo = uadd64_carry(a->frac_lo, b->frac_lo, &c); + r->frac_hi = uadd64_carry(a->frac_hi, b->frac_hi, &c); + return c; +} -static FloatParts round_canonical(FloatParts p, float_status *s, - const FloatFmt *parm) +static bool frac256_add(FloatParts256 *r, FloatParts256 *a, FloatParts256 *b) { - const uint64_t frac_lsb = parm->frac_lsb; - const uint64_t frac_lsbm1 = parm->frac_lsbm1; - const uint64_t round_mask = parm->round_mask; - const uint64_t roundeven_mask = parm->roundeven_mask; - const int exp_max = parm->exp_max; - const int frac_shift = parm->frac_shift; - uint64_t frac, inc; - int exp, flags = 0; - bool overflow_norm; + bool c = 0; + r->frac_lo = uadd64_carry(a->frac_lo, b->frac_lo, &c); + r->frac_lm = uadd64_carry(a->frac_lm, b->frac_lm, &c); + r->frac_hm = uadd64_carry(a->frac_hm, b->frac_hm, &c); + r->frac_hi = uadd64_carry(a->frac_hi, b->frac_hi, &c); + return c; +} - frac = p.frac; - exp = p.exp; +#define frac_add(R, A, B) FRAC_GENERIC_64_128_256(add, R)(R, A, B) - switch (p.cls) { - case float_class_normal: - switch (s->float_rounding_mode) { - case float_round_nearest_even: - overflow_norm = false; - inc = ((frac & roundeven_mask) != frac_lsbm1 ? frac_lsbm1 : 0); - break; - case float_round_ties_away: - overflow_norm = false; - inc = frac_lsbm1; - break; - case float_round_to_zero: - overflow_norm = true; - inc = 0; - break; - case float_round_up: - inc = p.sign ? 0 : round_mask; - overflow_norm = p.sign; - break; - case float_round_down: - inc = p.sign ? round_mask : 0; - overflow_norm = !p.sign; - break; - case float_round_to_odd: - overflow_norm = true; - inc = frac & frac_lsb ? 0 : round_mask; - break; - default: - g_assert_not_reached(); - } +static bool frac64_addi(FloatParts64 *r, FloatParts64 *a, uint64_t c) +{ + return uadd64_overflow(a->frac, c, &r->frac); +} - exp += parm->exp_bias; - if (likely(exp > 0)) { - if (frac & round_mask) { - flags |= float_flag_inexact; - frac += inc; - if (frac & DECOMPOSED_OVERFLOW_BIT) { - frac >>= 1; - exp++; - } - } - frac >>= frac_shift; - - if (parm->arm_althp) { - /* ARM Alt HP eschews Inf and NaN for a wider exponent. */ - if (unlikely(exp > exp_max)) { - /* Overflow. Return the maximum normal. */ - flags = float_flag_invalid; - exp = exp_max; - frac = -1; - } - } else if (unlikely(exp >= exp_max)) { - flags |= float_flag_overflow | float_flag_inexact; - if (overflow_norm) { - exp = exp_max - 1; - frac = -1; - } else { - p.cls = float_class_inf; - goto do_inf; - } - } - } else if (s->flush_to_zero) { - flags |= float_flag_output_denormal; - p.cls = float_class_zero; - goto do_zero; - } else { - bool is_tiny = s->tininess_before_rounding - || (exp < 0) - || !((frac + inc) & DECOMPOSED_OVERFLOW_BIT); - - shift64RightJamming(frac, 1 - exp, &frac); - if (frac & round_mask) { - /* Need to recompute round-to-even. */ - switch (s->float_rounding_mode) { - case float_round_nearest_even: - inc = ((frac & roundeven_mask) != frac_lsbm1 - ? frac_lsbm1 : 0); - break; - case float_round_to_odd: - inc = frac & frac_lsb ? 0 : round_mask; - break; - default: - break; - } - flags |= float_flag_inexact; - frac += inc; - } +static bool frac128_addi(FloatParts128 *r, FloatParts128 *a, uint64_t c) +{ + c = uadd64_overflow(a->frac_lo, c, &r->frac_lo); + return uadd64_overflow(a->frac_hi, c, &r->frac_hi); +} - exp = (frac & DECOMPOSED_IMPLICIT_BIT ? 1 : 0); - frac >>= frac_shift; +#define frac_addi(R, A, C) FRAC_GENERIC_64_128(addi, R)(R, A, C) - if (is_tiny && (flags & float_flag_inexact)) { - flags |= float_flag_underflow; - } - if (exp == 0 && frac == 0) { - p.cls = float_class_zero; - } - } - break; +static void frac64_allones(FloatParts64 *a) +{ + a->frac = -1; +} - case float_class_zero: - do_zero: - exp = 0; - frac = 0; - break; +static void frac128_allones(FloatParts128 *a) +{ + a->frac_hi = a->frac_lo = -1; +} - case float_class_inf: - do_inf: - assert(!parm->arm_althp); - exp = exp_max; - frac = 0; - break; +#define frac_allones(A) FRAC_GENERIC_64_128(allones, A)(A) - case float_class_qnan: - case float_class_snan: - assert(!parm->arm_althp); - exp = exp_max; - frac >>= parm->frac_shift; - break; +static int frac64_cmp(FloatParts64 *a, FloatParts64 *b) +{ + return a->frac == b->frac ? 0 : a->frac < b->frac ? -1 : 1; +} - default: - g_assert_not_reached(); +static int frac128_cmp(FloatParts128 *a, FloatParts128 *b) +{ + uint64_t ta = a->frac_hi, tb = b->frac_hi; + if (ta == tb) { + ta = a->frac_lo, tb = b->frac_lo; + if (ta == tb) { + return 0; + } } - - float_raise(flags, s); - p.exp = exp; - p.frac = frac; - return p; + return ta < tb ? -1 : 1; } -/* Explicit FloatFmt version */ -static FloatParts float16a_unpack_canonical(float16 f, float_status *s, - const FloatFmt *params) +#define frac_cmp(A, B) FRAC_GENERIC_64_128(cmp, A)(A, B) + +static void frac64_clear(FloatParts64 *a) { - return sf_canonicalize(float16_unpack_raw(f), params, s); + a->frac = 0; } -static FloatParts float16_unpack_canonical(float16 f, float_status *s) +static void frac128_clear(FloatParts128 *a) { - return float16a_unpack_canonical(f, s, &float16_params); + a->frac_hi = a->frac_lo = 0; } -static FloatParts bfloat16_unpack_canonical(bfloat16 f, float_status *s) +#define frac_clear(A) FRAC_GENERIC_64_128(clear, A)(A) + +static bool frac64_div(FloatParts64 *a, FloatParts64 *b) { - return sf_canonicalize(bfloat16_unpack_raw(f), &bfloat16_params, s); + uint64_t n1, n0, r, q; + bool ret; + + /* + * We want a 2*N / N-bit division to produce exactly an N-bit + * result, so that we do not lose any precision and so that we + * do not have to renormalize afterward. If A.frac < B.frac, + * then division would produce an (N-1)-bit result; shift A left + * by one to produce the an N-bit result, and return true to + * decrement the exponent to match. + * + * The udiv_qrnnd algorithm that we're using requires normalization, + * i.e. the msb of the denominator must be set, which is already true. + */ + ret = a->frac < b->frac; + if (ret) { + n0 = a->frac; + n1 = 0; + } else { + n0 = a->frac >> 1; + n1 = a->frac << 63; + } + q = udiv_qrnnd(&r, n0, n1, b->frac); + + /* Set lsb if there is a remainder, to set inexact. */ + a->frac = q | (r != 0); + + return ret; } -static float16 float16a_round_pack_canonical(FloatParts p, float_status *s, - const FloatFmt *params) +static bool frac128_div(FloatParts128 *a, FloatParts128 *b) { - return float16_pack_raw(round_canonical(p, s, params)); + uint64_t q0, q1, a0, a1, b0, b1; + uint64_t r0, r1, r2, r3, t0, t1, t2, t3; + bool ret = false; + + a0 = a->frac_hi, a1 = a->frac_lo; + b0 = b->frac_hi, b1 = b->frac_lo; + + ret = lt128(a0, a1, b0, b1); + if (!ret) { + a1 = shr_double(a0, a1, 1); + a0 = a0 >> 1; + } + + /* Use 128/64 -> 64 division as estimate for 192/128 -> 128 division. */ + q0 = estimateDiv128To64(a0, a1, b0); + + /* + * Estimate is high because B1 was not included (unless B1 == 0). + * Reduce quotient and increase remainder until remainder is non-negative. + * This loop will execute 0 to 2 times. + */ + mul128By64To192(b0, b1, q0, &t0, &t1, &t2); + sub192(a0, a1, 0, t0, t1, t2, &r0, &r1, &r2); + while (r0 != 0) { + q0--; + add192(r0, r1, r2, 0, b0, b1, &r0, &r1, &r2); + } + + /* Repeat using the remainder, producing a second word of quotient. */ + q1 = estimateDiv128To64(r1, r2, b0); + mul128By64To192(b0, b1, q1, &t1, &t2, &t3); + sub192(r1, r2, 0, t1, t2, t3, &r1, &r2, &r3); + while (r1 != 0) { + q1--; + add192(r1, r2, r3, 0, b0, b1, &r1, &r2, &r3); + } + + /* Any remainder indicates inexact; set sticky bit. */ + q1 |= (r2 | r3) != 0; + + a->frac_hi = q0; + a->frac_lo = q1; + return ret; } -static float16 float16_round_pack_canonical(FloatParts p, float_status *s) +#define frac_div(A, B) FRAC_GENERIC_64_128(div, A)(A, B) + +static bool frac64_eqz(FloatParts64 *a) { - return float16a_round_pack_canonical(p, s, &float16_params); + return a->frac == 0; } -static bfloat16 bfloat16_round_pack_canonical(FloatParts p, float_status *s) +static bool frac128_eqz(FloatParts128 *a) { - return bfloat16_pack_raw(round_canonical(p, s, &bfloat16_params)); + return (a->frac_hi | a->frac_lo) == 0; } -static FloatParts float32_unpack_canonical(float32 f, float_status *s) +#define frac_eqz(A) FRAC_GENERIC_64_128(eqz, A)(A) + +static void frac64_mulw(FloatParts128 *r, FloatParts64 *a, FloatParts64 *b) { - return sf_canonicalize(float32_unpack_raw(f), &float32_params, s); + mulu64(&r->frac_lo, &r->frac_hi, a->frac, b->frac); } -static float32 float32_round_pack_canonical(FloatParts p, float_status *s) +static void frac128_mulw(FloatParts256 *r, FloatParts128 *a, FloatParts128 *b) { - return float32_pack_raw(round_canonical(p, s, &float32_params)); + mul128To256(a->frac_hi, a->frac_lo, b->frac_hi, b->frac_lo, + &r->frac_hi, &r->frac_hm, &r->frac_lm, &r->frac_lo); } -static FloatParts float64_unpack_canonical(float64 f, float_status *s) +#define frac_mulw(R, A, B) FRAC_GENERIC_64_128(mulw, A)(R, A, B) + +static void frac64_neg(FloatParts64 *a) { - return sf_canonicalize(float64_unpack_raw(f), &float64_params, s); + a->frac = -a->frac; } -static float64 float64_round_pack_canonical(FloatParts p, float_status *s) +static void frac128_neg(FloatParts128 *a) { - return float64_pack_raw(round_canonical(p, s, &float64_params)); + bool c = 0; + a->frac_lo = usub64_borrow(0, a->frac_lo, &c); + a->frac_hi = usub64_borrow(0, a->frac_hi, &c); } -static FloatParts return_nan(FloatParts a, float_status *s) +static void frac256_neg(FloatParts256 *a) { - switch (a.cls) { - case float_class_snan: - s->float_exception_flags |= float_flag_invalid; - a = parts_silence_nan(a, s); - /* fall through */ - case float_class_qnan: - if (s->default_nan_mode) { - return parts_default_nan(s); - } - break; + bool c = 0; + a->frac_lo = usub64_borrow(0, a->frac_lo, &c); + a->frac_lm = usub64_borrow(0, a->frac_lm, &c); + a->frac_hm = usub64_borrow(0, a->frac_hm, &c); + a->frac_hi = usub64_borrow(0, a->frac_hi, &c); +} - default: - g_assert_not_reached(); +#define frac_neg(A) FRAC_GENERIC_64_128_256(neg, A)(A) + +static int frac64_normalize(FloatParts64 *a) +{ + if (a->frac) { + int shift = clz64(a->frac); + a->frac <<= shift; + return shift; } - return a; + return 64; } -static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) +static int frac128_normalize(FloatParts128 *a) { - if (is_snan(a.cls) || is_snan(b.cls)) { - s->float_exception_flags |= float_flag_invalid; + if (a->frac_hi) { + int shl = clz64(a->frac_hi); + a->frac_hi = shl_double(a->frac_hi, a->frac_lo, shl); + a->frac_lo <<= shl; + return shl; + } else if (a->frac_lo) { + int shl = clz64(a->frac_lo); + a->frac_hi = a->frac_lo << shl; + a->frac_lo = 0; + return shl + 64; } + return 128; +} - if (s->default_nan_mode) { - return parts_default_nan(s); +static int frac256_normalize(FloatParts256 *a) +{ + uint64_t a0 = a->frac_hi, a1 = a->frac_hm; + uint64_t a2 = a->frac_lm, a3 = a->frac_lo; + int ret, shl; + + if (likely(a0)) { + shl = clz64(a0); + if (shl == 0) { + return 0; + } + ret = shl; } else { - if (pickNaN(a.cls, b.cls, - a.frac > b.frac || - (a.frac == b.frac && a.sign < b.sign), s)) { - a = b; + if (a1) { + ret = 64; + a0 = a1, a1 = a2, a2 = a3, a3 = 0; + } else if (a2) { + ret = 128; + a0 = a2, a1 = a3, a2 = 0, a3 = 0; + } else if (a3) { + ret = 192; + a0 = a3, a1 = 0, a2 = 0, a3 = 0; + } else { + ret = 256; + a0 = 0, a1 = 0, a2 = 0, a3 = 0; + goto done; } - if (is_snan(a.cls)) { - return parts_silence_nan(a, s); + shl = clz64(a0); + if (shl == 0) { + goto done; } + ret += shl; } - return a; + + a0 = shl_double(a0, a1, shl); + a1 = shl_double(a1, a2, shl); + a2 = shl_double(a2, a3, shl); + a3 <<= shl; + + done: + a->frac_hi = a0; + a->frac_hm = a1; + a->frac_lm = a2; + a->frac_lo = a3; + return ret; } -static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, - bool inf_zero, float_status *s) +#define frac_normalize(A) FRAC_GENERIC_64_128_256(normalize, A)(A) + +static void frac64_shl(FloatParts64 *a, int c) { - int which; + a->frac <<= c; +} - if (is_snan(a.cls) || is_snan(b.cls) || is_snan(c.cls)) { - s->float_exception_flags |= float_flag_invalid; - } +static void frac128_shl(FloatParts128 *a, int c) +{ + uint64_t a0 = a->frac_hi, a1 = a->frac_lo; - which = pickNaNMulAdd(a.cls, b.cls, c.cls, inf_zero, s); + if (c & 64) { + a0 = a1, a1 = 0; + } - if (s->default_nan_mode) { - /* Note that this check is after pickNaNMulAdd so that function - * has an opportunity to set the Invalid flag. - */ - which = 3; + c &= 63; + if (c) { + a0 = shl_double(a0, a1, c); + a1 = a1 << c; } - switch (which) { - case 0: - break; - case 1: - a = b; - break; - case 2: - a = c; - break; - case 3: - return parts_default_nan(s); - default: - g_assert_not_reached(); + a->frac_hi = a0; + a->frac_lo = a1; +} + +#define frac_shl(A, C) FRAC_GENERIC_64_128(shl, A)(A, C) + +static void frac64_shr(FloatParts64 *a, int c) +{ + a->frac >>= c; +} + +static void frac128_shr(FloatParts128 *a, int c) +{ + uint64_t a0 = a->frac_hi, a1 = a->frac_lo; + + if (c & 64) { + a1 = a0, a0 = 0; } - if (is_snan(a.cls)) { - return parts_silence_nan(a, s); + c &= 63; + if (c) { + a1 = shr_double(a0, a1, c); + a0 = a0 >> c; } - return a; + + a->frac_hi = a0; + a->frac_lo = a1; } -/* - * Returns the result of adding or subtracting the values of the - * floating-point values `a' and `b'. The operation is performed - * according to the IEC/IEEE Standard for Binary Floating-Point - * Arithmetic. - */ +#define frac_shr(A, C) FRAC_GENERIC_64_128(shr, A)(A, C) -static FloatParts addsub_floats(FloatParts a, FloatParts b, bool subtract, - float_status *s) +static void frac64_shrjam(FloatParts64 *a, int c) { - bool a_sign = a.sign; - bool b_sign = b.sign ^ subtract; - - if (a_sign != b_sign) { - /* Subtraction */ - - if (a.cls == float_class_normal && b.cls == float_class_normal) { - if (a.exp > b.exp || (a.exp == b.exp && a.frac >= b.frac)) { - shift64RightJamming(b.frac, a.exp - b.exp, &b.frac); - a.frac = a.frac - b.frac; - } else { - shift64RightJamming(a.frac, b.exp - a.exp, &a.frac); - a.frac = b.frac - a.frac; - a.exp = b.exp; - a_sign ^= 1; - } + uint64_t a0 = a->frac; - if (a.frac == 0) { - a.cls = float_class_zero; - a.sign = s->float_rounding_mode == float_round_down; - } else { - int shift = clz64(a.frac) - 1; - a.frac = a.frac << shift; - a.exp = a.exp - shift; - a.sign = a_sign; - } - return a; - } - if (is_nan(a.cls) || is_nan(b.cls)) { - return pick_nan(a, b, s); - } - if (a.cls == float_class_inf) { - if (b.cls == float_class_inf) { - float_raise(float_flag_invalid, s); - return parts_default_nan(s); - } - return a; - } - if (a.cls == float_class_zero && b.cls == float_class_zero) { - a.sign = s->float_rounding_mode == float_round_down; - return a; - } - if (a.cls == float_class_zero || b.cls == float_class_inf) { - b.sign = a_sign ^ 1; - return b; + if (likely(c != 0)) { + if (likely(c < 64)) { + a0 = (a0 >> c) | (shr_double(a0, 0, c) != 0); + } else { + a0 = a0 != 0; } - if (b.cls == float_class_zero) { - return a; + a->frac = a0; + } +} + +static void frac128_shrjam(FloatParts128 *a, int c) +{ + uint64_t a0 = a->frac_hi, a1 = a->frac_lo; + uint64_t sticky = 0; + + if (unlikely(c == 0)) { + return; + } else if (likely(c < 64)) { + /* nothing */ + } else if (likely(c < 128)) { + sticky = a1; + a1 = a0; + a0 = 0; + c &= 63; + if (c == 0) { + goto done; } } else { - /* Addition */ - if (a.cls == float_class_normal && b.cls == float_class_normal) { - if (a.exp > b.exp) { - shift64RightJamming(b.frac, a.exp - b.exp, &b.frac); - } else if (a.exp < b.exp) { - shift64RightJamming(a.frac, b.exp - a.exp, &a.frac); - a.exp = b.exp; - } - a.frac += b.frac; - if (a.frac & DECOMPOSED_OVERFLOW_BIT) { - shift64RightJamming(a.frac, 1, &a.frac); - a.exp += 1; - } - return a; - } - if (is_nan(a.cls) || is_nan(b.cls)) { - return pick_nan(a, b, s); + sticky = a0 | a1; + a0 = a1 = 0; + goto done; + } + + sticky |= shr_double(a1, 0, c); + a1 = shr_double(a0, a1, c); + a0 = a0 >> c; + + done: + a->frac_lo = a1 | (sticky != 0); + a->frac_hi = a0; +} + +static void frac256_shrjam(FloatParts256 *a, int c) +{ + uint64_t a0 = a->frac_hi, a1 = a->frac_hm; + uint64_t a2 = a->frac_lm, a3 = a->frac_lo; + uint64_t sticky = 0; + + if (unlikely(c == 0)) { + return; + } else if (likely(c < 64)) { + /* nothing */ + } else if (likely(c < 256)) { + if (unlikely(c & 128)) { + sticky |= a2 | a3; + a3 = a1, a2 = a0, a1 = 0, a0 = 0; } - if (a.cls == float_class_inf || b.cls == float_class_zero) { - return a; + if (unlikely(c & 64)) { + sticky |= a3; + a3 = a2, a2 = a1, a1 = a0, a0 = 0; } - if (b.cls == float_class_inf || a.cls == float_class_zero) { - b.sign = b_sign; - return b; + c &= 63; + if (c == 0) { + goto done; } + } else { + sticky = a0 | a1 | a2 | a3; + a0 = a1 = a2 = a3 = 0; + goto done; } - g_assert_not_reached(); + + sticky |= shr_double(a3, 0, c); + a3 = shr_double(a2, a3, c); + a2 = shr_double(a1, a2, c); + a1 = shr_double(a0, a1, c); + a0 = a0 >> c; + + done: + a->frac_lo = a3 | (sticky != 0); + a->frac_lm = a2; + a->frac_hm = a1; + a->frac_hi = a0; +} + +#define frac_shrjam(A, C) FRAC_GENERIC_64_128_256(shrjam, A)(A, C) + +static bool frac64_sub(FloatParts64 *r, FloatParts64 *a, FloatParts64 *b) +{ + return usub64_overflow(a->frac, b->frac, &r->frac); +} + +static bool frac128_sub(FloatParts128 *r, FloatParts128 *a, FloatParts128 *b) +{ + bool c = 0; + r->frac_lo = usub64_borrow(a->frac_lo, b->frac_lo, &c); + r->frac_hi = usub64_borrow(a->frac_hi, b->frac_hi, &c); + return c; +} + +static bool frac256_sub(FloatParts256 *r, FloatParts256 *a, FloatParts256 *b) +{ + bool c = 0; + r->frac_lo = usub64_borrow(a->frac_lo, b->frac_lo, &c); + r->frac_lm = usub64_borrow(a->frac_lm, b->frac_lm, &c); + r->frac_hm = usub64_borrow(a->frac_hm, b->frac_hm, &c); + r->frac_hi = usub64_borrow(a->frac_hi, b->frac_hi, &c); + return c; +} + +#define frac_sub(R, A, B) FRAC_GENERIC_64_128_256(sub, R)(R, A, B) + +static void frac64_truncjam(FloatParts64 *r, FloatParts128 *a) +{ + r->frac = a->frac_hi | (a->frac_lo != 0); } +static void frac128_truncjam(FloatParts128 *r, FloatParts256 *a) +{ + r->frac_hi = a->frac_hi; + r->frac_lo = a->frac_hm | ((a->frac_lm | a->frac_lo) != 0); +} + +#define frac_truncjam(R, A) FRAC_GENERIC_64_128(truncjam, R)(R, A) + +static void frac64_widen(FloatParts128 *r, FloatParts64 *a) +{ + r->frac_hi = a->frac; + r->frac_lo = 0; +} + +static void frac128_widen(FloatParts256 *r, FloatParts128 *a) +{ + r->frac_hi = a->frac_hi; + r->frac_hm = a->frac_lo; + r->frac_lm = 0; + r->frac_lo = 0; +} + +#define frac_widen(A, B) FRAC_GENERIC_64_128(widen, B)(A, B) + +#define partsN(NAME) glue(glue(glue(parts,N),_),NAME) +#define FloatPartsN glue(FloatParts,N) +#define FloatPartsW glue(FloatParts,W) + +#define N 64 +#define W 128 + +#include "softfloat-parts-addsub.c.inc" +#include "softfloat-parts.c.inc" + +#undef N +#undef W +#define N 128 +#define W 256 + +#include "softfloat-parts-addsub.c.inc" +#include "softfloat-parts.c.inc" + +#undef N +#undef W +#define N 256 + +#include "softfloat-parts-addsub.c.inc" + +#undef N +#undef W +#undef partsN +#undef FloatPartsN +#undef FloatPartsW + /* - * Returns the result of adding or subtracting the floating-point - * values `a' and `b'. The operation is performed according to the - * IEC/IEEE Standard for Binary Floating-Point Arithmetic. + * Pack/unpack routines with a specific FloatFmt. */ -float16 QEMU_FLATTEN float16_add(float16 a, float16 b, float_status *status) +static void float16a_unpack_canonical(FloatParts64 *p, float16 f, + float_status *s, const FloatFmt *params) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pb = float16_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, false, status); + float16_unpack_raw(p, f); + parts_canonicalize(p, s, params); +} - return float16_round_pack_canonical(pr, status); +static void float16_unpack_canonical(FloatParts64 *p, float16 f, + float_status *s) +{ + float16a_unpack_canonical(p, f, s, &float16_params); } -float16 QEMU_FLATTEN float16_sub(float16 a, float16 b, float_status *status) +static void bfloat16_unpack_canonical(FloatParts64 *p, bfloat16 f, + float_status *s) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pb = float16_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); + bfloat16_unpack_raw(p, f); + parts_canonicalize(p, s, &bfloat16_params); +} + +static float16 float16a_round_pack_canonical(FloatParts64 *p, + float_status *s, + const FloatFmt *params) +{ + parts_uncanon(p, s, params); + return float16_pack_raw(p); +} + +static float16 float16_round_pack_canonical(FloatParts64 *p, + float_status *s) +{ + return float16a_round_pack_canonical(p, s, &float16_params); +} + +static bfloat16 bfloat16_round_pack_canonical(FloatParts64 *p, + float_status *s) +{ + parts_uncanon(p, s, &bfloat16_params); + return bfloat16_pack_raw(p); +} + +static void float32_unpack_canonical(FloatParts64 *p, float32 f, + float_status *s) +{ + float32_unpack_raw(p, f); + parts_canonicalize(p, s, &float32_params); +} + +static float32 float32_round_pack_canonical(FloatParts64 *p, + float_status *s) +{ + parts_uncanon(p, s, &float32_params); + return float32_pack_raw(p); +} + +static void float64_unpack_canonical(FloatParts64 *p, float64 f, + float_status *s) +{ + float64_unpack_raw(p, f); + parts_canonicalize(p, s, &float64_params); +} + +static float64 float64_round_pack_canonical(FloatParts64 *p, + float_status *s) +{ + parts_uncanon(p, s, &float64_params); + return float64_pack_raw(p); +} + +static void float128_unpack_canonical(FloatParts128 *p, float128 f, + float_status *s) +{ + float128_unpack_raw(p, f); + parts_canonicalize(p, s, &float128_params); +} + +static float128 float128_round_pack_canonical(FloatParts128 *p, + float_status *s) +{ + parts_uncanon(p, s, &float128_params); + return float128_pack_raw(p); +} + +/* + * Addition and subtraction + */ + +static float16 QEMU_FLATTEN +float16_addsub(float16 a, float16 b, float_status *status, bool subtract) +{ + FloatParts64 pa, pb, *pr; + + float16_unpack_canonical(&pa, a, status); + float16_unpack_canonical(&pb, b, status); + pr = parts_addsub(&pa, &pb, status, subtract); return float16_round_pack_canonical(pr, status); } +float16 float16_add(float16 a, float16 b, float_status *status) +{ + return float16_addsub(a, b, status, false); +} + +float16 float16_sub(float16 a, float16 b, float_status *status) +{ + return float16_addsub(a, b, status, true); +} + static float32 QEMU_SOFTFLOAT_ATTR -soft_f32_addsub(float32 a, float32 b, bool subtract, float_status *status) +soft_f32_addsub(float32 a, float32 b, float_status *status, bool subtract) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, subtract, status); + FloatParts64 pa, pb, *pr; + + float32_unpack_canonical(&pa, a, status); + float32_unpack_canonical(&pb, b, status); + pr = parts_addsub(&pa, &pb, status, subtract); return float32_round_pack_canonical(pr, status); } -static inline float32 soft_f32_add(float32 a, float32 b, float_status *status) +static float32 soft_f32_add(float32 a, float32 b, float_status *status) { - return soft_f32_addsub(a, b, false, status); + return soft_f32_addsub(a, b, status, false); } -static inline float32 soft_f32_sub(float32 a, float32 b, float_status *status) +static float32 soft_f32_sub(float32 a, float32 b, float_status *status) { - return soft_f32_addsub(a, b, true, status); + return soft_f32_addsub(a, b, status, true); } static float64 QEMU_SOFTFLOAT_ATTR -soft_f64_addsub(float64 a, float64 b, bool subtract, float_status *status) +soft_f64_addsub(float64 a, float64 b, float_status *status, bool subtract) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, subtract, status); + FloatParts64 pa, pb, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + pr = parts_addsub(&pa, &pb, status, subtract); return float64_round_pack_canonical(pr, status); } -static inline float64 soft_f64_add(float64 a, float64 b, float_status *status) +static float64 soft_f64_add(float64 a, float64 b, float_status *status) { - return soft_f64_addsub(a, b, false, status); + return soft_f64_addsub(a, b, status, false); } -static inline float64 soft_f64_sub(float64 a, float64 b, float_status *status) +static float64 soft_f64_sub(float64 a, float64 b, float_status *status) { - return soft_f64_addsub(a, b, true, status); + return soft_f64_addsub(a, b, status, true); } static float hard_f32_add(float a, float b) @@ -1182,82 +1591,61 @@ float64_sub(float64 a, float64 b, float_status *s) return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub); } -/* - * Returns the result of adding or subtracting the bfloat16 - * values `a' and `b'. - */ -bfloat16 QEMU_FLATTEN bfloat16_add(bfloat16 a, bfloat16 b, float_status *status) +static bfloat16 QEMU_FLATTEN +bfloat16_addsub(bfloat16 a, bfloat16 b, float_status *status, bool subtract) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pb = bfloat16_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, false, status); + FloatParts64 pa, pb, *pr; + + bfloat16_unpack_canonical(&pa, a, status); + bfloat16_unpack_canonical(&pb, b, status); + pr = parts_addsub(&pa, &pb, status, subtract); return bfloat16_round_pack_canonical(pr, status); } -bfloat16 QEMU_FLATTEN bfloat16_sub(bfloat16 a, bfloat16 b, float_status *status) +bfloat16 bfloat16_add(bfloat16 a, bfloat16 b, float_status *status) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pb = bfloat16_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); - - return bfloat16_round_pack_canonical(pr, status); + return bfloat16_addsub(a, b, status, false); } -/* - * Returns the result of multiplying the floating-point values `a' and - * `b'. The operation is performed according to the IEC/IEEE Standard - * for Binary Floating-Point Arithmetic. - */ +bfloat16 bfloat16_sub(bfloat16 a, bfloat16 b, float_status *status) +{ + return bfloat16_addsub(a, b, status, true); +} -static FloatParts mul_floats(FloatParts a, FloatParts b, float_status *s) +static float128 QEMU_FLATTEN +float128_addsub(float128 a, float128 b, float_status *status, bool subtract) { - bool sign = a.sign ^ b.sign; + FloatParts128 pa, pb, *pr; - if (a.cls == float_class_normal && b.cls == float_class_normal) { - uint64_t hi, lo; - int exp = a.exp + b.exp; + float128_unpack_canonical(&pa, a, status); + float128_unpack_canonical(&pb, b, status); + pr = parts_addsub(&pa, &pb, status, subtract); - mul64To128(a.frac, b.frac, &hi, &lo); - shift128RightJamming(hi, lo, DECOMPOSED_BINARY_POINT, &hi, &lo); - if (lo & DECOMPOSED_OVERFLOW_BIT) { - shift64RightJamming(lo, 1, &lo); - exp += 1; - } + return float128_round_pack_canonical(pr, status); +} - /* Re-use a */ - a.exp = exp; - a.sign = sign; - a.frac = lo; - return a; - } - /* handle all the NaN cases */ - if (is_nan(a.cls) || is_nan(b.cls)) { - return pick_nan(a, b, s); - } - /* Inf * Zero == NaN */ - if ((a.cls == float_class_inf && b.cls == float_class_zero) || - (a.cls == float_class_zero && b.cls == float_class_inf)) { - s->float_exception_flags |= float_flag_invalid; - return parts_default_nan(s); - } - /* Multiply by 0 or Inf */ - if (a.cls == float_class_inf || a.cls == float_class_zero) { - a.sign = sign; - return a; - } - if (b.cls == float_class_inf || b.cls == float_class_zero) { - b.sign = sign; - return b; - } - g_assert_not_reached(); +float128 float128_add(float128 a, float128 b, float_status *status) +{ + return float128_addsub(a, b, status, false); } +float128 float128_sub(float128 a, float128 b, float_status *status) +{ + return float128_addsub(a, b, status, true); +} + +/* + * Multiplication + */ + float16 QEMU_FLATTEN float16_mul(float16 a, float16 b, float_status *status) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pb = float16_unpack_canonical(b, status); - FloatParts pr = mul_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + float16_unpack_canonical(&pa, a, status); + float16_unpack_canonical(&pb, b, status); + pr = parts_mul(&pa, &pb, status); return float16_round_pack_canonical(pr, status); } @@ -1265,9 +1653,11 @@ float16 QEMU_FLATTEN float16_mul(float16 a, float16 b, float_status *status) static float32 QEMU_SOFTFLOAT_ATTR soft_f32_mul(float32 a, float32 b, float_status *status) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = mul_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + float32_unpack_canonical(&pa, a, status); + float32_unpack_canonical(&pb, b, status); + pr = parts_mul(&pa, &pb, status); return float32_round_pack_canonical(pr, status); } @@ -1275,9 +1665,11 @@ soft_f32_mul(float32 a, float32 b, float_status *status) static float64 QEMU_SOFTFLOAT_ATTR soft_f64_mul(float64 a, float64 b, float_status *status) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = mul_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + pr = parts_mul(&pa, &pb, status); return float64_round_pack_canonical(pr, status); } @@ -1306,230 +1698,43 @@ float64_mul(float64 a, float64 b, float_status *s) f64_is_zon2, f64_addsubmul_post); } -/* - * Returns the result of multiplying the bfloat16 - * values `a' and `b'. - */ - -bfloat16 QEMU_FLATTEN bfloat16_mul(bfloat16 a, bfloat16 b, float_status *status) +bfloat16 QEMU_FLATTEN +bfloat16_mul(bfloat16 a, bfloat16 b, float_status *status) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pb = bfloat16_unpack_canonical(b, status); - FloatParts pr = mul_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + bfloat16_unpack_canonical(&pa, a, status); + bfloat16_unpack_canonical(&pb, b, status); + pr = parts_mul(&pa, &pb, status); return bfloat16_round_pack_canonical(pr, status); } -/* - * Returns the result of multiplying the floating-point values `a' and - * `b' then adding 'c', with no intermediate rounding step after the - * multiplication. The operation is performed according to the - * IEC/IEEE Standard for Binary Floating-Point Arithmetic 754-2008. - * The flags argument allows the caller to select negation of the - * addend, the intermediate product, or the final result. (The - * difference between this and having the caller do a separate - * negation is that negating externally will flip the sign bit on - * NaNs.) - */ - -static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c, - int flags, float_status *s) -{ - bool inf_zero = ((1 << a.cls) | (1 << b.cls)) == - ((1 << float_class_inf) | (1 << float_class_zero)); - bool p_sign; - bool sign_flip = flags & float_muladd_negate_result; - FloatClass p_class; - uint64_t hi, lo; - int p_exp; - - /* It is implementation-defined whether the cases of (0,inf,qnan) - * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN - * they return if they do), so we have to hand this information - * off to the target-specific pick-a-NaN routine. - */ - if (is_nan(a.cls) || is_nan(b.cls) || is_nan(c.cls)) { - return pick_nan_muladd(a, b, c, inf_zero, s); - } - - if (inf_zero) { - s->float_exception_flags |= float_flag_invalid; - return parts_default_nan(s); - } - - if (flags & float_muladd_negate_c) { - c.sign ^= 1; - } - - p_sign = a.sign ^ b.sign; - - if (flags & float_muladd_negate_product) { - p_sign ^= 1; - } - - if (a.cls == float_class_inf || b.cls == float_class_inf) { - p_class = float_class_inf; - } else if (a.cls == float_class_zero || b.cls == float_class_zero) { - p_class = float_class_zero; - } else { - p_class = float_class_normal; - } - - if (c.cls == float_class_inf) { - if (p_class == float_class_inf && p_sign != c.sign) { - s->float_exception_flags |= float_flag_invalid; - return parts_default_nan(s); - } else { - a.cls = float_class_inf; - a.sign = c.sign ^ sign_flip; - return a; - } - } - - if (p_class == float_class_inf) { - a.cls = float_class_inf; - a.sign = p_sign ^ sign_flip; - return a; - } - - if (p_class == float_class_zero) { - if (c.cls == float_class_zero) { - if (p_sign != c.sign) { - p_sign = s->float_rounding_mode == float_round_down; - } - c.sign = p_sign; - } else if (flags & float_muladd_halve_result) { - c.exp -= 1; - } - c.sign ^= sign_flip; - return c; - } - - /* a & b should be normals now... */ - assert(a.cls == float_class_normal && - b.cls == float_class_normal); - - p_exp = a.exp + b.exp; - - /* Multiply of 2 62-bit numbers produces a (2*62) == 124-bit - * result. - */ - mul64To128(a.frac, b.frac, &hi, &lo); - /* binary point now at bit 124 */ - - /* check for overflow */ - if (hi & (1ULL << (DECOMPOSED_BINARY_POINT * 2 + 1 - 64))) { - shift128RightJamming(hi, lo, 1, &hi, &lo); - p_exp += 1; - } - - /* + add/sub */ - if (c.cls == float_class_zero) { - /* move binary point back to 62 */ - shift128RightJamming(hi, lo, DECOMPOSED_BINARY_POINT, &hi, &lo); - } else { - int exp_diff = p_exp - c.exp; - if (p_sign == c.sign) { - /* Addition */ - if (exp_diff <= 0) { - shift128RightJamming(hi, lo, - DECOMPOSED_BINARY_POINT - exp_diff, - &hi, &lo); - lo += c.frac; - p_exp = c.exp; - } else { - uint64_t c_hi, c_lo; - /* shift c to the same binary point as the product (124) */ - c_hi = c.frac >> 2; - c_lo = 0; - shift128RightJamming(c_hi, c_lo, - exp_diff, - &c_hi, &c_lo); - add128(hi, lo, c_hi, c_lo, &hi, &lo); - /* move binary point back to 62 */ - shift128RightJamming(hi, lo, DECOMPOSED_BINARY_POINT, &hi, &lo); - } - - if (lo & DECOMPOSED_OVERFLOW_BIT) { - shift64RightJamming(lo, 1, &lo); - p_exp += 1; - } - - } else { - /* Subtraction */ - uint64_t c_hi, c_lo; - /* make C binary point match product at bit 124 */ - c_hi = c.frac >> 2; - c_lo = 0; - - if (exp_diff <= 0) { - shift128RightJamming(hi, lo, -exp_diff, &hi, &lo); - if (exp_diff == 0 - && - (hi > c_hi || (hi == c_hi && lo >= c_lo))) { - sub128(hi, lo, c_hi, c_lo, &hi, &lo); - } else { - sub128(c_hi, c_lo, hi, lo, &hi, &lo); - p_sign ^= 1; - p_exp = c.exp; - } - } else { - shift128RightJamming(c_hi, c_lo, - exp_diff, - &c_hi, &c_lo); - sub128(hi, lo, c_hi, c_lo, &hi, &lo); - } - - if (hi == 0 && lo == 0) { - a.cls = float_class_zero; - a.sign = s->float_rounding_mode == float_round_down; - a.sign ^= sign_flip; - return a; - } else { - int shift; - if (hi != 0) { - shift = clz64(hi); - } else { - shift = clz64(lo) + 64; - } - /* Normalizing to a binary point of 124 is the - correct adjust for the exponent. However since we're - shifting, we might as well put the binary point back - at 62 where we really want it. Therefore shift as - if we're leaving 1 bit at the top of the word, but - adjust the exponent as if we're leaving 3 bits. */ - shift -= 1; - if (shift >= 64) { - lo = lo << (shift - 64); - } else { - hi = (hi << shift) | (lo >> (64 - shift)); - lo = hi | ((lo << shift) != 0); - } - p_exp -= shift - 2; - } - } - } - - if (flags & float_muladd_halve_result) { - p_exp -= 1; - } +float128 QEMU_FLATTEN +float128_mul(float128 a, float128 b, float_status *status) +{ + FloatParts128 pa, pb, *pr; - /* finally prepare our result */ - a.cls = float_class_normal; - a.sign = p_sign ^ sign_flip; - a.exp = p_exp; - a.frac = lo; + float128_unpack_canonical(&pa, a, status); + float128_unpack_canonical(&pb, b, status); + pr = parts_mul(&pa, &pb, status); - return a; + return float128_round_pack_canonical(pr, status); } +/* + * Fused multiply-add + */ + float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c, - int flags, float_status *status) + int flags, float_status *status) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pb = float16_unpack_canonical(b, status); - FloatParts pc = float16_unpack_canonical(c, status); - FloatParts pr = muladd_floats(pa, pb, pc, flags, status); + FloatParts64 pa, pb, pc, *pr; + + float16_unpack_canonical(&pa, a, status); + float16_unpack_canonical(&pb, b, status); + float16_unpack_canonical(&pc, c, status); + pr = parts_muladd(&pa, &pb, &pc, flags, status); return float16_round_pack_canonical(pr, status); } @@ -1538,10 +1743,12 @@ static float32 QEMU_SOFTFLOAT_ATTR soft_f32_muladd(float32 a, float32 b, float32 c, int flags, float_status *status) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pc = float32_unpack_canonical(c, status); - FloatParts pr = muladd_floats(pa, pb, pc, flags, status); + FloatParts64 pa, pb, pc, *pr; + + float32_unpack_canonical(&pa, a, status); + float32_unpack_canonical(&pb, b, status); + float32_unpack_canonical(&pc, c, status); + pr = parts_muladd(&pa, &pb, &pc, flags, status); return float32_round_pack_canonical(pr, status); } @@ -1550,10 +1757,12 @@ static float64 QEMU_SOFTFLOAT_ATTR soft_f64_muladd(float64 a, float64 b, float64 c, int flags, float_status *status) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pc = float64_unpack_canonical(c, status); - FloatParts pr = muladd_floats(pa, pb, pc, flags, status); + FloatParts64 pa, pb, pc, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + float64_unpack_canonical(&pc, c, status); + pr = parts_muladd(&pa, &pb, &pc, flags, status); return float64_round_pack_canonical(pr, status); } @@ -1615,7 +1824,7 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) ur.h = fmaf(ua.h, ub.h, uc.h); if (unlikely(f32_is_inf(ur))) { - s->float_exception_flags |= float_flag_overflow; + float_raise(float_flag_overflow, s); } else if (unlikely(fabsf(ur.h) <= FLT_MIN)) { ua = ua_orig; uc = uc_orig; @@ -1686,7 +1895,7 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) ur.h = fma(ua.h, ub.h, uc.h); if (unlikely(f64_is_inf(ur))) { - s->float_exception_flags |= float_flag_overflow; + float_raise(float_flag_overflow, s); } else if (unlikely(fabs(ur.h) <= FLT_MIN)) { ua = ua_orig; uc = uc_orig; @@ -1702,107 +1911,43 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s); } -/* - * Returns the result of multiplying the bfloat16 values `a' - * and `b' then adding 'c', with no intermediate rounding step after the - * multiplication. - */ - bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c, int flags, float_status *status) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pb = bfloat16_unpack_canonical(b, status); - FloatParts pc = bfloat16_unpack_canonical(c, status); - FloatParts pr = muladd_floats(pa, pb, pc, flags, status); + FloatParts64 pa, pb, pc, *pr; + + bfloat16_unpack_canonical(&pa, a, status); + bfloat16_unpack_canonical(&pb, b, status); + bfloat16_unpack_canonical(&pc, c, status); + pr = parts_muladd(&pa, &pb, &pc, flags, status); return bfloat16_round_pack_canonical(pr, status); } -/* - * Returns the result of dividing the floating-point value `a' by the - * corresponding value `b'. The operation is performed according to - * the IEC/IEEE Standard for Binary Floating-Point Arithmetic. - */ - -static FloatParts div_floats(FloatParts a, FloatParts b, float_status *s) +float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c, + int flags, float_status *status) { - bool sign = a.sign ^ b.sign; + FloatParts128 pa, pb, pc, *pr; - if (a.cls == float_class_normal && b.cls == float_class_normal) { - uint64_t n0, n1, q, r; - int exp = a.exp - b.exp; + float128_unpack_canonical(&pa, a, status); + float128_unpack_canonical(&pb, b, status); + float128_unpack_canonical(&pc, c, status); + pr = parts_muladd(&pa, &pb, &pc, flags, status); - /* - * We want a 2*N / N-bit division to produce exactly an N-bit - * result, so that we do not lose any precision and so that we - * do not have to renormalize afterward. If A.frac < B.frac, - * then division would produce an (N-1)-bit result; shift A left - * by one to produce the an N-bit result, and decrement the - * exponent to match. - * - * The udiv_qrnnd algorithm that we're using requires normalization, - * i.e. the msb of the denominator must be set. Since we know that - * DECOMPOSED_BINARY_POINT is msb-1, the inputs must be shifted left - * by one (more), and the remainder must be shifted right by one. - */ - if (a.frac < b.frac) { - exp -= 1; - shift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 2, &n1, &n0); - } else { - shift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 1, &n1, &n0); - } - q = udiv_qrnnd(&r, n1, n0, b.frac << 1); - - /* - * Set lsb if there is a remainder, to set inexact. - * As mentioned above, to find the actual value of the remainder we - * would need to shift right, but (1) we are only concerned about - * non-zero-ness, and (2) the remainder will always be even because - * both inputs to the division primitive are even. - */ - a.frac = q | (r != 0); - a.sign = sign; - a.exp = exp; - return a; - } - /* handle all the NaN cases */ - if (is_nan(a.cls) || is_nan(b.cls)) { - return pick_nan(a, b, s); - } - /* 0/0 or Inf/Inf */ - if (a.cls == b.cls - && - (a.cls == float_class_inf || a.cls == float_class_zero)) { - s->float_exception_flags |= float_flag_invalid; - return parts_default_nan(s); - } - /* Inf / x or 0 / x */ - if (a.cls == float_class_inf || a.cls == float_class_zero) { - a.sign = sign; - return a; - } - /* Div 0 => Inf */ - if (b.cls == float_class_zero) { - s->float_exception_flags |= float_flag_divbyzero; - a.cls = float_class_inf; - a.sign = sign; - return a; - } - /* Div by Inf */ - if (b.cls == float_class_inf) { - a.cls = float_class_zero; - a.sign = sign; - return a; - } - g_assert_not_reached(); + return float128_round_pack_canonical(pr, status); } +/* + * Division + */ + float16 float16_div(float16 a, float16 b, float_status *status) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pb = float16_unpack_canonical(b, status); - FloatParts pr = div_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + float16_unpack_canonical(&pa, a, status); + float16_unpack_canonical(&pb, b, status); + pr = parts_div(&pa, &pb, status); return float16_round_pack_canonical(pr, status); } @@ -1810,9 +1955,11 @@ float16 float16_div(float16 a, float16 b, float_status *status) static float32 QEMU_SOFTFLOAT_ATTR soft_f32_div(float32 a, float32 b, float_status *status) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = div_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + float32_unpack_canonical(&pa, a, status); + float32_unpack_canonical(&pb, b, status); + pr = parts_div(&pa, &pb, status); return float32_round_pack_canonical(pr, status); } @@ -1820,9 +1967,11 @@ soft_f32_div(float32 a, float32 b, float_status *status) static float64 QEMU_SOFTFLOAT_ATTR soft_f64_div(float64 a, float64 b, float_status *status) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = div_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + pr = parts_div(&pa, &pb, status); return float64_round_pack_canonical(pr, status); } @@ -1885,20 +2034,30 @@ float64_div(float64 a, float64 b, float_status *s) f64_div_pre, f64_div_post); } -/* - * Returns the result of dividing the bfloat16 - * value `a' by the corresponding value `b'. - */ - -bfloat16 bfloat16_div(bfloat16 a, bfloat16 b, float_status *status) +bfloat16 QEMU_FLATTEN +bfloat16_div(bfloat16 a, bfloat16 b, float_status *status) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pb = bfloat16_unpack_canonical(b, status); - FloatParts pr = div_floats(pa, pb, status); + FloatParts64 pa, pb, *pr; + + bfloat16_unpack_canonical(&pa, a, status); + bfloat16_unpack_canonical(&pb, b, status); + pr = parts_div(&pa, &pb, status); return bfloat16_round_pack_canonical(pr, status); } +float128 QEMU_FLATTEN +float128_div(float128 a, float128 b, float_status *status) +{ + FloatParts128 pa, pb, *pr; + + float128_unpack_canonical(&pa, a, status); + float128_unpack_canonical(&pb, b, status); + pr = parts_div(&pa, &pb, status); + + return float128_round_pack_canonical(pr, status); +} + /* * Float to Float conversions * @@ -1906,81 +2065,134 @@ bfloat16 bfloat16_div(bfloat16 a, bfloat16 b, float_status *status) * conversion is performed according to the IEC/IEEE Standard for * Binary Floating-Point Arithmetic. * - * The float_to_float helper only needs to take care of raising - * invalid exceptions and handling the conversion on NaNs. + * Usually this only needs to take care of raising invalid exceptions + * and handling the conversion on NaNs. */ -static FloatParts float_to_float(FloatParts a, const FloatFmt *dstf, - float_status *s) +static void parts_float_to_ahp(FloatParts64 *a, float_status *s) { - if (dstf->arm_althp) { - switch (a.cls) { - case float_class_qnan: - case float_class_snan: - /* There is no NaN in the destination format. Raise Invalid - * and return a zero with the sign of the input NaN. - */ - s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_zero; - a.frac = 0; - a.exp = 0; - break; + switch (a->cls) { + case float_class_qnan: + case float_class_snan: + /* + * There is no NaN in the destination format. Raise Invalid + * and return a zero with the sign of the input NaN. + */ + float_raise(float_flag_invalid, s); + a->cls = float_class_zero; + break; - case float_class_inf: - /* There is no Inf in the destination format. Raise Invalid - * and return the maximum normal with the correct sign. - */ - s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_normal; - a.exp = dstf->exp_max; - a.frac = ((1ull << dstf->frac_size) - 1) << dstf->frac_shift; - break; + case float_class_inf: + /* + * There is no Inf in the destination format. Raise Invalid + * and return the maximum normal with the correct sign. + */ + float_raise(float_flag_invalid, s); + a->cls = float_class_normal; + a->exp = float16_params_ahp.exp_max; + a->frac = MAKE_64BIT_MASK(float16_params_ahp.frac_shift, + float16_params_ahp.frac_size + 1); + break; - default: - break; - } - } else if (is_nan(a.cls)) { - if (is_snan(a.cls)) { - s->float_exception_flags |= float_flag_invalid; - a = parts_silence_nan(a, s); - } - if (s->default_nan_mode) { - return parts_default_nan(s); - } + case float_class_normal: + case float_class_zero: + break; + + default: + g_assert_not_reached(); + } +} + +static void parts64_float_to_float(FloatParts64 *a, float_status *s) +{ + if (is_nan(a->cls)) { + parts_return_nan(a, s); + } +} + +static void parts128_float_to_float(FloatParts128 *a, float_status *s) +{ + if (is_nan(a->cls)) { + parts_return_nan(a, s); + } +} + +#define parts_float_to_float(P, S) \ + PARTS_GENERIC_64_128(float_to_float, P)(P, S) + +static void parts_float_to_float_narrow(FloatParts64 *a, FloatParts128 *b, + float_status *s) +{ + a->cls = b->cls; + a->sign = b->sign; + a->exp = b->exp; + + if (a->cls == float_class_normal) { + frac_truncjam(a, b); + } else if (is_nan(a->cls)) { + /* Discard the low bits of the NaN. */ + a->frac = b->frac_hi; + parts_return_nan(a, s); + } +} + +static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b, + float_status *s) +{ + a->cls = b->cls; + a->sign = b->sign; + a->exp = b->exp; + frac_widen(a, b); + + if (is_nan(a->cls)) { + parts_return_nan(a, s); } - return a; } float32 float16_to_float32(float16 a, bool ieee, float_status *s) { const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; - FloatParts p = float16a_unpack_canonical(a, s, fmt16); - FloatParts pr = float_to_float(p, &float32_params, s); - return float32_round_pack_canonical(pr, s); + FloatParts64 p; + + float16a_unpack_canonical(&p, a, s, fmt16); + parts_float_to_float(&p, s); + return float32_round_pack_canonical(&p, s); } float64 float16_to_float64(float16 a, bool ieee, float_status *s) { const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; - FloatParts p = float16a_unpack_canonical(a, s, fmt16); - FloatParts pr = float_to_float(p, &float64_params, s); - return float64_round_pack_canonical(pr, s); + FloatParts64 p; + + float16a_unpack_canonical(&p, a, s, fmt16); + parts_float_to_float(&p, s); + return float64_round_pack_canonical(&p, s); } float16 float32_to_float16(float32 a, bool ieee, float_status *s) { - const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; - FloatParts p = float32_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, fmt16, s); - return float16a_round_pack_canonical(pr, s, fmt16); + FloatParts64 p; + const FloatFmt *fmt; + + float32_unpack_canonical(&p, a, s); + if (ieee) { + parts_float_to_float(&p, s); + fmt = &float16_params; + } else { + parts_float_to_ahp(&p, s); + fmt = &float16_params_ahp; + } + return float16a_round_pack_canonical(&p, s, fmt); } static float64 QEMU_SOFTFLOAT_ATTR soft_float32_to_float64(float32 a, float_status *s) { - FloatParts p = float32_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, &float64_params, s); - return float64_round_pack_canonical(pr, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + parts_float_to_float(&p, s); + return float64_round_pack_canonical(&p, s); } float64 float32_to_float64(float32 a, float_status *s) @@ -2001,313 +2213,291 @@ float64 float32_to_float64(float32 a, float_status *s) float16 float64_to_float16(float64 a, bool ieee, float_status *s) { - const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; - FloatParts p = float64_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, fmt16, s); - return float16a_round_pack_canonical(pr, s, fmt16); + FloatParts64 p; + const FloatFmt *fmt; + + float64_unpack_canonical(&p, a, s); + if (ieee) { + parts_float_to_float(&p, s); + fmt = &float16_params; + } else { + parts_float_to_ahp(&p, s); + fmt = &float16_params_ahp; + } + return float16a_round_pack_canonical(&p, s, fmt); } float32 float64_to_float32(float64 a, float_status *s) { - FloatParts p = float64_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, &float32_params, s); - return float32_round_pack_canonical(pr, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + parts_float_to_float(&p, s); + return float32_round_pack_canonical(&p, s); } float32 bfloat16_to_float32(bfloat16 a, float_status *s) { - FloatParts p = bfloat16_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, &float32_params, s); - return float32_round_pack_canonical(pr, s); + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + parts_float_to_float(&p, s); + return float32_round_pack_canonical(&p, s); } float64 bfloat16_to_float64(bfloat16 a, float_status *s) { - FloatParts p = bfloat16_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, &float64_params, s); - return float64_round_pack_canonical(pr, s); + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + parts_float_to_float(&p, s); + return float64_round_pack_canonical(&p, s); } bfloat16 float32_to_bfloat16(float32 a, float_status *s) { - FloatParts p = float32_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, &bfloat16_params, s); - return bfloat16_round_pack_canonical(pr, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + parts_float_to_float(&p, s); + return bfloat16_round_pack_canonical(&p, s); } bfloat16 float64_to_bfloat16(float64 a, float_status *s) { - FloatParts p = float64_unpack_canonical(a, s); - FloatParts pr = float_to_float(p, &bfloat16_params, s); - return bfloat16_round_pack_canonical(pr, s); -} + FloatParts64 p; -/* - * Rounds the floating-point value `a' to an integer, and returns the - * result as a floating-point value. The operation is performed - * according to the IEC/IEEE Standard for Binary Floating-Point - * Arithmetic. - */ + float64_unpack_canonical(&p, a, s); + parts_float_to_float(&p, s); + return bfloat16_round_pack_canonical(&p, s); +} -static FloatParts round_to_int(FloatParts a, FloatRoundMode rmode, - int scale, float_status *s) +float32 float128_to_float32(float128 a, float_status *s) { - switch (a.cls) { - case float_class_qnan: - case float_class_snan: - return return_nan(a, s); + FloatParts64 p64; + FloatParts128 p128; - case float_class_zero: - case float_class_inf: - /* already "integral" */ - break; + float128_unpack_canonical(&p128, a, s); + parts_float_to_float_narrow(&p64, &p128, s); + return float32_round_pack_canonical(&p64, s); +} - case float_class_normal: - scale = MIN(MAX(scale, -0x10000), 0x10000); - a.exp += scale; +float64 float128_to_float64(float128 a, float_status *s) +{ + FloatParts64 p64; + FloatParts128 p128; - if (a.exp >= DECOMPOSED_BINARY_POINT) { - /* already integral */ - break; - } - if (a.exp < 0) { - bool one; - /* all fractional */ - s->float_exception_flags |= float_flag_inexact; - switch (rmode) { - case float_round_nearest_even: - one = a.exp == -1 && a.frac > DECOMPOSED_IMPLICIT_BIT; - break; - case float_round_ties_away: - one = a.exp == -1 && a.frac >= DECOMPOSED_IMPLICIT_BIT; - break; - case float_round_to_zero: - one = false; - break; - case float_round_up: - one = !a.sign; - break; - case float_round_down: - one = a.sign; - break; - case float_round_to_odd: - one = true; - break; - default: - g_assert_not_reached(); - } + float128_unpack_canonical(&p128, a, s); + parts_float_to_float_narrow(&p64, &p128, s); + return float64_round_pack_canonical(&p64, s); +} - if (one) { - a.frac = DECOMPOSED_IMPLICIT_BIT; - a.exp = 0; - } else { - a.cls = float_class_zero; - } - } else { - uint64_t frac_lsb = DECOMPOSED_IMPLICIT_BIT >> a.exp; - uint64_t frac_lsbm1 = frac_lsb >> 1; - uint64_t rnd_even_mask = (frac_lsb - 1) | frac_lsb; - uint64_t rnd_mask = rnd_even_mask >> 1; - uint64_t inc; +float128 float32_to_float128(float32 a, float_status *s) +{ + FloatParts64 p64; + FloatParts128 p128; - switch (rmode) { - case float_round_nearest_even: - inc = ((a.frac & rnd_even_mask) != frac_lsbm1 ? frac_lsbm1 : 0); - break; - case float_round_ties_away: - inc = frac_lsbm1; - break; - case float_round_to_zero: - inc = 0; - break; - case float_round_up: - inc = a.sign ? 0 : rnd_mask; - break; - case float_round_down: - inc = a.sign ? rnd_mask : 0; - break; - case float_round_to_odd: - inc = a.frac & frac_lsb ? 0 : rnd_mask; - break; - default: - g_assert_not_reached(); - } + float32_unpack_canonical(&p64, a, s); + parts_float_to_float_widen(&p128, &p64, s); + return float128_round_pack_canonical(&p128, s); +} - if (a.frac & rnd_mask) { - s->float_exception_flags |= float_flag_inexact; - a.frac += inc; - a.frac &= ~rnd_mask; - if (a.frac & DECOMPOSED_OVERFLOW_BIT) { - a.frac >>= 1; - a.exp++; - } - } - } - break; - default: - g_assert_not_reached(); - } - return a; +float128 float64_to_float128(float64 a, float_status *s) +{ + FloatParts64 p64; + FloatParts128 p128; + + float64_unpack_canonical(&p64, a, s); + parts_float_to_float_widen(&p128, &p64, s); + return float128_round_pack_canonical(&p128, s); } +/* + * Round to integral value + */ + float16 float16_round_to_int(float16 a, float_status *s) { - FloatParts pa = float16_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); - return float16_round_pack_canonical(pr, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + parts_round_to_int(&p, s->float_rounding_mode, 0, s, &float16_params); + return float16_round_pack_canonical(&p, s); } float32 float32_round_to_int(float32 a, float_status *s) { - FloatParts pa = float32_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); - return float32_round_pack_canonical(pr, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + parts_round_to_int(&p, s->float_rounding_mode, 0, s, &float32_params); + return float32_round_pack_canonical(&p, s); } float64 float64_round_to_int(float64 a, float_status *s) { - FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); - return float64_round_pack_canonical(pr, s); -} + FloatParts64 p; -/* - * Rounds the bfloat16 value `a' to an integer, and returns the - * result as a bfloat16 value. - */ + float64_unpack_canonical(&p, a, s); + parts_round_to_int(&p, s->float_rounding_mode, 0, s, &float64_params); + return float64_round_pack_canonical(&p, s); +} bfloat16 bfloat16_round_to_int(bfloat16 a, float_status *s) { - FloatParts pa = bfloat16_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); - return bfloat16_round_pack_canonical(pr, s); -} + FloatParts64 p; -/* - * Returns the result of converting the floating-point value `a' to - * the two's complement integer format. The conversion is performed - * according to the IEC/IEEE Standard for Binary Floating-Point - * Arithmetic---which means in particular that the conversion is - * rounded according to the current rounding mode. If `a' is a NaN, - * the largest positive integer is returned. Otherwise, if the - * conversion overflows, the largest integer with the same sign as `a' - * is returned. -*/ + bfloat16_unpack_canonical(&p, a, s); + parts_round_to_int(&p, s->float_rounding_mode, 0, s, &bfloat16_params); + return bfloat16_round_pack_canonical(&p, s); +} -static int64_t round_to_int_and_pack(FloatParts in, FloatRoundMode rmode, - int scale, int64_t min, int64_t max, - float_status *s) +float128 float128_round_to_int(float128 a, float_status *s) { - uint64_t r; - int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, scale, s); + FloatParts128 p; - switch (p.cls) { - case float_class_snan: - case float_class_qnan: - s->float_exception_flags = orig_flags | float_flag_invalid; - return max; - case float_class_inf: - s->float_exception_flags = orig_flags | float_flag_invalid; - return p.sign ? min : max; - case float_class_zero: - return 0; - case float_class_normal: - if (p.exp < DECOMPOSED_BINARY_POINT) { - r = p.frac >> (DECOMPOSED_BINARY_POINT - p.exp); - } else if (p.exp - DECOMPOSED_BINARY_POINT < 2) { - r = p.frac << (p.exp - DECOMPOSED_BINARY_POINT); - } else { - r = UINT64_MAX; - } - if (p.sign) { - if (r <= -(uint64_t) min) { - return -r; - } else { - s->float_exception_flags = orig_flags | float_flag_invalid; - return min; - } - } else { - if (r <= max) { - return r; - } else { - s->float_exception_flags = orig_flags | float_flag_invalid; - return max; - } - } - default: - g_assert_not_reached(); - } + float128_unpack_canonical(&p, a, s); + parts_round_to_int(&p, s->float_rounding_mode, 0, s, &float128_params); + return float128_round_pack_canonical(&p, s); } +/* + * Floating-point to signed integer conversions + */ + int8_t float16_to_int8_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float16_unpack_canonical(a, s), - rmode, scale, INT8_MIN, INT8_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT8_MIN, INT8_MAX, s); } int16_t float16_to_int16_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float16_unpack_canonical(a, s), - rmode, scale, INT16_MIN, INT16_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT16_MIN, INT16_MAX, s); } int32_t float16_to_int32_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float16_unpack_canonical(a, s), - rmode, scale, INT32_MIN, INT32_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT32_MIN, INT32_MAX, s); } int64_t float16_to_int64_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float16_unpack_canonical(a, s), - rmode, scale, INT64_MIN, INT64_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); } int16_t float32_to_int16_scalbn(float32 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float32_unpack_canonical(a, s), - rmode, scale, INT16_MIN, INT16_MAX, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT16_MIN, INT16_MAX, s); } int32_t float32_to_int32_scalbn(float32 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float32_unpack_canonical(a, s), - rmode, scale, INT32_MIN, INT32_MAX, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT32_MIN, INT32_MAX, s); } int64_t float32_to_int64_scalbn(float32 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float32_unpack_canonical(a, s), - rmode, scale, INT64_MIN, INT64_MAX, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); } int16_t float64_to_int16_scalbn(float64 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float64_unpack_canonical(a, s), - rmode, scale, INT16_MIN, INT16_MAX, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT16_MIN, INT16_MAX, s); } int32_t float64_to_int32_scalbn(float64 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float64_unpack_canonical(a, s), - rmode, scale, INT32_MIN, INT32_MAX, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT32_MIN, INT32_MAX, s); } int64_t float64_to_int64_scalbn(float64 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_int_and_pack(float64_unpack_canonical(a, s), - rmode, scale, INT64_MIN, INT64_MAX, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t bfloat16_to_int16_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, + float_status *s) +{ + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t bfloat16_to_int32_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, + float_status *s) +{ + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t bfloat16_to_int64_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, + float_status *s) +{ + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); +} + +static int32_t float128_to_int32_scalbn(float128 a, FloatRoundMode rmode, + int scale, float_status *s) +{ + FloatParts128 p; + + float128_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT32_MIN, INT32_MAX, s); +} + +static int64_t float128_to_int64_scalbn(float128 a, FloatRoundMode rmode, + int scale, float_status *s) +{ + FloatParts128 p; + + float128_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); } int8_t float16_to_int8(float16 a, float_status *s) @@ -2360,6 +2550,16 @@ int64_t float64_to_int64(float64 a, float_status *s) return float64_to_int64_scalbn(a, s->float_rounding_mode, 0, s); } +int32_t float128_to_int32(float128 a, float_status *s) +{ + return float128_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float128_to_int64(float128 a, float_status *s) +{ + return float128_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + int16_t float16_to_int16_round_to_zero(float16 a, float_status *s) { return float16_to_int16_scalbn(a, float_round_to_zero, 0, s); @@ -2405,30 +2605,14 @@ int64_t float64_to_int64_round_to_zero(float64 a, float_status *s) return float64_to_int64_scalbn(a, float_round_to_zero, 0, s); } -/* - * Returns the result of converting the floating-point value `a' to - * the two's complement integer format. - */ - -int16_t bfloat16_to_int16_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, - float_status *s) -{ - return round_to_int_and_pack(bfloat16_unpack_canonical(a, s), - rmode, scale, INT16_MIN, INT16_MAX, s); -} - -int32_t bfloat16_to_int32_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, - float_status *s) +int32_t float128_to_int32_round_to_zero(float128 a, float_status *s) { - return round_to_int_and_pack(bfloat16_unpack_canonical(a, s), - rmode, scale, INT32_MIN, INT32_MAX, s); + return float128_to_int32_scalbn(a, float_round_to_zero, 0, s); } -int64_t bfloat16_to_int64_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, - float_status *s) +int64_t float128_to_int64_round_to_zero(float128 a, float_status *s) { - return round_to_int_and_pack(bfloat16_unpack_canonical(a, s), - rmode, scale, INT64_MIN, INT64_MAX, s); + return float128_to_int64_scalbn(a, float_round_to_zero, 0, s); } int16_t bfloat16_to_int16(bfloat16 a, float_status *s) @@ -2474,121 +2658,149 @@ int64_t bfloat16_to_int64_round_to_zero(bfloat16 a, float_status *s) * flag. */ -static uint64_t round_to_uint_and_pack(FloatParts in, FloatRoundMode rmode, +static uint64_t round_to_uint_and_pack(FloatParts64 p, FloatRoundMode rmode, int scale, uint64_t max, float_status *s) { - int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, scale, s); + int flags = 0; uint64_t r; switch (p.cls) { case float_class_snan: case float_class_qnan: - s->float_exception_flags = orig_flags | float_flag_invalid; - return max; + flags = float_flag_invalid; + r = max; + break; + case float_class_inf: - s->float_exception_flags = orig_flags | float_flag_invalid; - return p.sign ? 0 : max; + flags = float_flag_invalid; + r = p.sign ? 0 : max; + break; + case float_class_zero: return 0; + case float_class_normal: - if (p.sign) { - s->float_exception_flags = orig_flags | float_flag_invalid; - return 0; + /* TODO: 62 = N - 2, frac_size for rounding */ + if (parts_round_to_int_normal(&p, rmode, scale, 62)) { + flags = float_flag_inexact; + if (p.cls == float_class_zero) { + r = 0; + break; + } } - if (p.exp < DECOMPOSED_BINARY_POINT) { - r = p.frac >> (DECOMPOSED_BINARY_POINT - p.exp); - } else if (p.exp - DECOMPOSED_BINARY_POINT < 2) { - r = p.frac << (p.exp - DECOMPOSED_BINARY_POINT); + if (p.sign) { + flags = float_flag_invalid; + r = 0; + } else if (p.exp > DECOMPOSED_BINARY_POINT) { + flags = float_flag_invalid; + r = max; } else { - s->float_exception_flags = orig_flags | float_flag_invalid; - return max; + r = p.frac >> (DECOMPOSED_BINARY_POINT - p.exp); + if (r > max) { + flags = float_flag_invalid; + r = max; + } } + break; - /* For uint64 this will never trip, but if p.exp is too large - * to shift a decomposed fraction we shall have exited via the - * 3rd leg above. - */ - if (r > max) { - s->float_exception_flags = orig_flags | float_flag_invalid; - return max; - } - return r; default: g_assert_not_reached(); } + + float_raise(flags, s); + return r; } uint8_t float16_to_uint8_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float16_unpack_canonical(a, s), - rmode, scale, UINT8_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT8_MAX, s); } uint16_t float16_to_uint16_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float16_unpack_canonical(a, s), - rmode, scale, UINT16_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT16_MAX, s); } uint32_t float16_to_uint32_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float16_unpack_canonical(a, s), - rmode, scale, UINT32_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT32_MAX, s); } uint64_t float16_to_uint64_scalbn(float16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float16_unpack_canonical(a, s), - rmode, scale, UINT64_MAX, s); + FloatParts64 p; + + float16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT64_MAX, s); } uint16_t float32_to_uint16_scalbn(float32 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float32_unpack_canonical(a, s), - rmode, scale, UINT16_MAX, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT16_MAX, s); } uint32_t float32_to_uint32_scalbn(float32 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float32_unpack_canonical(a, s), - rmode, scale, UINT32_MAX, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT32_MAX, s); } uint64_t float32_to_uint64_scalbn(float32 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float32_unpack_canonical(a, s), - rmode, scale, UINT64_MAX, s); + FloatParts64 p; + + float32_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT64_MAX, s); } uint16_t float64_to_uint16_scalbn(float64 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float64_unpack_canonical(a, s), - rmode, scale, UINT16_MAX, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT16_MAX, s); } uint32_t float64_to_uint32_scalbn(float64 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float64_unpack_canonical(a, s), - rmode, scale, UINT32_MAX, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT32_MAX, s); } uint64_t float64_to_uint64_scalbn(float64 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(float64_unpack_canonical(a, s), - rmode, scale, UINT64_MAX, s); + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT64_MAX, s); } uint8_t float16_to_uint8(float16 a, float_status *s) @@ -2694,22 +2906,28 @@ uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *s) uint16_t bfloat16_to_uint16_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(bfloat16_unpack_canonical(a, s), - rmode, scale, UINT16_MAX, s); + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT16_MAX, s); } uint32_t bfloat16_to_uint32_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(bfloat16_unpack_canonical(a, s), - rmode, scale, UINT32_MAX, s); + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT32_MAX, s); } uint64_t bfloat16_to_uint64_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, float_status *s) { - return round_to_uint_and_pack(bfloat16_unpack_canonical(a, s), - rmode, scale, UINT64_MAX, s); + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return round_to_uint_and_pack(p, rmode, scale, UINT64_MAX, s); } uint16_t bfloat16_to_uint16(bfloat16 a, float_status *s) @@ -2750,9 +2968,9 @@ uint64_t bfloat16_to_uint64_round_to_zero(bfloat16 a, float_status *s) * to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts int_to_float(int64_t a, int scale, float_status *status) +static FloatParts64 int_to_float(int64_t a, int scale, float_status *status) { - FloatParts r = { .sign = false }; + FloatParts64 r = { .sign = false }; if (a == 0) { r.cls = float_class_zero; @@ -2765,11 +2983,11 @@ static FloatParts int_to_float(int64_t a, int scale, float_status *status) f = -f; r.sign = true; } - shift = clz64(f) - 1; + shift = clz64(f); scale = MIN(MAX(scale, -0x10000), 0x10000); r.exp = DECOMPOSED_BINARY_POINT - shift + scale; - r.frac = (shift < 0 ? DECOMPOSED_IMPLICIT_BIT : f << shift); + r.frac = f << shift; } return r; @@ -2777,8 +2995,8 @@ static FloatParts int_to_float(int64_t a, int scale, float_status *status) float16 int64_to_float16_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, scale, status); - return float16_round_pack_canonical(pa, status); + FloatParts64 pa = int_to_float(a, scale, status); + return float16_round_pack_canonical(&pa, status); } float16 int32_to_float16_scalbn(int32_t a, int scale, float_status *status) @@ -2813,8 +3031,8 @@ float16 int8_to_float16(int8_t a, float_status *status) float32 int64_to_float32_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, scale, status); - return float32_round_pack_canonical(pa, status); + FloatParts64 pa = int_to_float(a, scale, status); + return float32_round_pack_canonical(&pa, status); } float32 int32_to_float32_scalbn(int32_t a, int scale, float_status *status) @@ -2844,8 +3062,8 @@ float32 int16_to_float32(int16_t a, float_status *status) float64 int64_to_float64_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, scale, status); - return float64_round_pack_canonical(pa, status); + FloatParts64 pa = int_to_float(a, scale, status); + return float64_round_pack_canonical(&pa, status); } float64 int32_to_float64_scalbn(int32_t a, int scale, float_status *status) @@ -2880,8 +3098,8 @@ float64 int16_to_float64(int16_t a, float_status *status) bfloat16 int64_to_bfloat16_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, scale, status); - return bfloat16_round_pack_canonical(pa, status); + FloatParts64 pa = int_to_float(a, scale, status); + return bfloat16_round_pack_canonical(&pa, status); } bfloat16 int32_to_bfloat16_scalbn(int32_t a, int scale, float_status *status) @@ -2917,24 +3135,19 @@ bfloat16 int16_to_bfloat16(int16_t a, float_status *status) * IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts uint_to_float(uint64_t a, int scale, float_status *status) +static FloatParts64 uint_to_float(uint64_t a, int scale, float_status *status) { - FloatParts r = { .sign = false }; + FloatParts64 r = { .sign = false }; + int shift; if (a == 0) { r.cls = float_class_zero; } else { scale = MIN(MAX(scale, -0x10000), 0x10000); + shift = clz64(a); r.cls = float_class_normal; - if ((int64_t)a < 0) { - r.exp = DECOMPOSED_BINARY_POINT + 1 + scale; - shift64RightJamming(a, 1, &a); - r.frac = a; - } else { - int shift = clz64(a) - 1; - r.exp = DECOMPOSED_BINARY_POINT - shift + scale; - r.frac = a << shift; - } + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = a << shift; } return r; @@ -2942,8 +3155,8 @@ static FloatParts uint_to_float(uint64_t a, int scale, float_status *status) float16 uint64_to_float16_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, scale, status); - return float16_round_pack_canonical(pa, status); + FloatParts64 pa = uint_to_float(a, scale, status); + return float16_round_pack_canonical(&pa, status); } float16 uint32_to_float16_scalbn(uint32_t a, int scale, float_status *status) @@ -2978,8 +3191,8 @@ float16 uint8_to_float16(uint8_t a, float_status *status) float32 uint64_to_float32_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, scale, status); - return float32_round_pack_canonical(pa, status); + FloatParts64 pa = uint_to_float(a, scale, status); + return float32_round_pack_canonical(&pa, status); } float32 uint32_to_float32_scalbn(uint32_t a, int scale, float_status *status) @@ -3009,8 +3222,8 @@ float32 uint16_to_float32(uint16_t a, float_status *status) float64 uint64_to_float64_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, scale, status); - return float64_round_pack_canonical(pa, status); + FloatParts64 pa = uint_to_float(a, scale, status); + return float64_round_pack_canonical(&pa, status); } float64 uint32_to_float64_scalbn(uint32_t a, int scale, float_status *status) @@ -3045,8 +3258,8 @@ float64 uint16_to_float64(uint16_t a, float_status *status) bfloat16 uint64_to_bfloat16_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, scale, status); - return bfloat16_round_pack_canonical(pa, status); + FloatParts64 pa = uint_to_float(a, scale, status); + return bfloat16_round_pack_canonical(&pa, status); } bfloat16 uint32_to_bfloat16_scalbn(uint32_t a, int scale, float_status *status) @@ -3090,7 +3303,7 @@ bfloat16 uint16_to_bfloat16(uint16_t a, float_status *status) * minnummag() and maxnummag() functions correspond to minNumMag() * and minNumMag() from the IEEE-754 2008. */ -static FloatParts minmax_floats(FloatParts a, FloatParts b, bool ismin, +static FloatParts64 minmax_floats(FloatParts64 a, FloatParts64 b, bool ismin, bool ieee, bool ismag, float_status *s) { if (unlikely(is_nan(a.cls) || is_nan(b.cls))) { @@ -3101,14 +3314,14 @@ static FloatParts minmax_floats(FloatParts a, FloatParts b, bool ismin, * the invalid exception is raised. */ if (is_snan(a.cls) || is_snan(b.cls)) { - return pick_nan(a, b, s); + return *parts_pick_nan(&a, &b, s); } else if (is_nan(a.cls) && !is_nan(b.cls)) { return b; } else if (is_nan(b.cls) && !is_nan(a.cls)) { return a; } } - return pick_nan(a, b, s); + return *parts_pick_nan(&a, &b, s); } else { int a_exp, b_exp; @@ -3165,11 +3378,11 @@ static FloatParts minmax_floats(FloatParts a, FloatParts b, bool ismin, float ## sz float ## sz ## _ ## name(float ## sz a, float ## sz b, \ float_status *s) \ { \ - FloatParts pa = float ## sz ## _unpack_canonical(a, s); \ - FloatParts pb = float ## sz ## _unpack_canonical(b, s); \ - FloatParts pr = minmax_floats(pa, pb, ismin, isiee, ismag, s); \ - \ - return float ## sz ## _round_pack_canonical(pr, s); \ + FloatParts64 pa, pb, pr; \ + float ## sz ## _unpack_canonical(&pa, a, s); \ + float ## sz ## _unpack_canonical(&pb, b, s); \ + pr = minmax_floats(pa, pb, ismin, isiee, ismag, s); \ + return float ## sz ## _round_pack_canonical(&pr, s); \ } MINMAX(16, min, true, false, false) @@ -3198,11 +3411,11 @@ MINMAX(64, maxnummag, false, true, true) #define BF16_MINMAX(name, ismin, isiee, ismag) \ bfloat16 bfloat16_ ## name(bfloat16 a, bfloat16 b, float_status *s) \ { \ - FloatParts pa = bfloat16_unpack_canonical(a, s); \ - FloatParts pb = bfloat16_unpack_canonical(b, s); \ - FloatParts pr = minmax_floats(pa, pb, ismin, isiee, ismag, s); \ - \ - return bfloat16_round_pack_canonical(pr, s); \ + FloatParts64 pa, pb, pr; \ + bfloat16_unpack_canonical(&pa, a, s); \ + bfloat16_unpack_canonical(&pb, b, s); \ + pr = minmax_floats(pa, pb, ismin, isiee, ismag, s); \ + return bfloat16_round_pack_canonical(&pr, s); \ } BF16_MINMAX(min, true, false, false) @@ -3215,14 +3428,14 @@ BF16_MINMAX(maxnummag, false, true, true) #undef BF16_MINMAX /* Floating point compare */ -static FloatRelation compare_floats(FloatParts a, FloatParts b, bool is_quiet, +static FloatRelation compare_floats(FloatParts64 a, FloatParts64 b, bool is_quiet, float_status *s) { if (is_nan(a.cls) || is_nan(b.cls)) { if (!is_quiet || a.cls == float_class_snan || b.cls == float_class_snan) { - s->float_exception_flags |= float_flag_invalid; + float_raise(float_flag_invalid, s); } return float_relation_unordered; } @@ -3276,8 +3489,9 @@ static FloatRelation compare_floats(FloatParts a, FloatParts b, bool is_quiet, static int attr \ name(float ## sz a, float ## sz b, bool is_quiet, float_status *s) \ { \ - FloatParts pa = float ## sz ## _unpack_canonical(a, s); \ - FloatParts pb = float ## sz ## _unpack_canonical(b, s); \ + FloatParts64 pa, pb; \ + float ## sz ## _unpack_canonical(&pa, a, s); \ + float ## sz ## _unpack_canonical(&pb, b, s); \ return compare_floats(pa, pb, is_quiet, s); \ } @@ -3378,8 +3592,10 @@ FloatRelation float64_compare_quiet(float64 a, float64 b, float_status *s) static FloatRelation QEMU_FLATTEN soft_bf16_compare(bfloat16 a, bfloat16 b, bool is_quiet, float_status *s) { - FloatParts pa = bfloat16_unpack_canonical(a, s); - FloatParts pb = bfloat16_unpack_canonical(b, s); + FloatParts64 pa, pb; + + bfloat16_unpack_canonical(&pa, a, s); + bfloat16_unpack_canonical(&pb, b, s); return compare_floats(pa, pb, is_quiet, s); } @@ -3394,16 +3610,16 @@ FloatRelation bfloat16_compare_quiet(bfloat16 a, bfloat16 b, float_status *s) } /* Multiply A by 2 raised to the power N. */ -static FloatParts scalbn_decomposed(FloatParts a, int n, float_status *s) +static FloatParts64 scalbn_decomposed(FloatParts64 a, int n, float_status *s) { if (unlikely(is_nan(a.cls))) { - return return_nan(a, s); + parts_return_nan(&a, s); } if (a.cls == float_class_normal) { - /* The largest float type (even though not supported by FloatParts) + /* The largest float type (even though not supported by FloatParts64) * is float128, which has a 15 bit exponent. Bounding N to 16 bits * still allows rounding to infinity, without allowing overflow - * within the int32_t that backs FloatParts.exp. + * within the int32_t that backs FloatParts64.exp. */ n = MIN(MAX(n, -0x10000), 0x10000); a.exp += n; @@ -3413,30 +3629,38 @@ static FloatParts scalbn_decomposed(FloatParts a, int n, float_status *s) float16 float16_scalbn(float16 a, int n, float_status *status) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pr = scalbn_decomposed(pa, n, status); - return float16_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + float16_unpack_canonical(&pa, a, status); + pr = scalbn_decomposed(pa, n, status); + return float16_round_pack_canonical(&pr, status); } float32 float32_scalbn(float32 a, int n, float_status *status) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pr = scalbn_decomposed(pa, n, status); - return float32_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + float32_unpack_canonical(&pa, a, status); + pr = scalbn_decomposed(pa, n, status); + return float32_round_pack_canonical(&pr, status); } float64 float64_scalbn(float64 a, int n, float_status *status) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pr = scalbn_decomposed(pa, n, status); - return float64_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + float64_unpack_canonical(&pa, a, status); + pr = scalbn_decomposed(pa, n, status); + return float64_round_pack_canonical(&pr, status); } bfloat16 bfloat16_scalbn(bfloat16 a, int n, float_status *status) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pr = scalbn_decomposed(pa, n, status); - return bfloat16_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + bfloat16_unpack_canonical(&pa, a, status); + pr = scalbn_decomposed(pa, n, status); + return bfloat16_round_pack_canonical(&pr, status); } /* @@ -3451,20 +3675,22 @@ bfloat16 bfloat16_scalbn(bfloat16 a, int n, float_status *status) * especially for 64 bit floats. */ -static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) +static FloatParts64 sqrt_float(FloatParts64 a, float_status *s, const FloatFmt *p) { uint64_t a_frac, r_frac, s_frac; int bit, last_bit; if (is_nan(a.cls)) { - return return_nan(a, s); + parts_return_nan(&a, s); + return a; } if (a.cls == float_class_zero) { return a; /* sqrt(+-0) = +-0 */ } if (a.sign) { - s->float_exception_flags |= float_flag_invalid; - return parts_default_nan(s); + float_raise(float_flag_invalid, s); + parts_default_nan(&a, s); + return a; } if (a.cls == float_class_inf) { return a; /* sqrt(+inf) = +inf */ @@ -3475,12 +3701,9 @@ static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) /* We need two overflow bits at the top. Adding room for that is a * right shift. If the exponent is odd, we can discard the low bit * by multiplying the fraction by 2; that's a left shift. Combine - * those and we shift right if the exponent is even. + * those and we shift right by 1 if the exponent is odd, otherwise 2. */ - a_frac = a.frac; - if (!(a.exp & 1)) { - a_frac >>= 1; - } + a_frac = a.frac >> (2 - (a.exp & 1)); a.exp >>= 1; /* Bit-by-bit computation of sqrt. */ @@ -3488,10 +3711,10 @@ static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) s_frac = 0; /* Iterate from implicit bit down to the 3 extra bits to compute a - * properly rounded result. Remember we've inserted one more bit - * at the top, so these positions are one less. + * properly rounded result. Remember we've inserted two more bits + * at the top, so these positions are two less. */ - bit = DECOMPOSED_BINARY_POINT - 1; + bit = DECOMPOSED_BINARY_POINT - 2; last_bit = MAX(p->frac_shift - 4, 0); do { uint64_t q = 1ULL << bit; @@ -3507,32 +3730,38 @@ static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) /* Undo the right shift done above. If there is any remaining * fraction, the result is inexact. Set the sticky bit. */ - a.frac = (r_frac << 1) + (a_frac != 0); + a.frac = (r_frac << 2) + (a_frac != 0); return a; } float16 QEMU_FLATTEN float16_sqrt(float16 a, float_status *status) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pr = sqrt_float(pa, status, &float16_params); - return float16_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + float16_unpack_canonical(&pa, a, status); + pr = sqrt_float(pa, status, &float16_params); + return float16_round_pack_canonical(&pr, status); } static float32 QEMU_SOFTFLOAT_ATTR soft_f32_sqrt(float32 a, float_status *status) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pr = sqrt_float(pa, status, &float32_params); - return float32_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + float32_unpack_canonical(&pa, a, status); + pr = sqrt_float(pa, status, &float32_params); + return float32_round_pack_canonical(&pr, status); } static float64 QEMU_SOFTFLOAT_ATTR soft_f64_sqrt(float64 a, float_status *status) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pr = sqrt_float(pa, status, &float64_params); - return float64_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + float64_unpack_canonical(&pa, a, status); + pr = sqrt_float(pa, status, &float64_params); + return float64_round_pack_canonical(&pr, status); } float32 QEMU_FLATTEN float32_sqrt(float32 xa, float_status *s) @@ -3591,9 +3820,11 @@ float64 QEMU_FLATTEN float64_sqrt(float64 xa, float_status *s) bfloat16 QEMU_FLATTEN bfloat16_sqrt(bfloat16 a, float_status *status) { - FloatParts pa = bfloat16_unpack_canonical(a, status); - FloatParts pr = sqrt_float(pa, status, &bfloat16_params); - return bfloat16_round_pack_canonical(pr, status); + FloatParts64 pa, pr; + + bfloat16_unpack_canonical(&pa, a, status); + pr = sqrt_float(pa, status, &bfloat16_params); + return bfloat16_round_pack_canonical(&pr, status); } /*---------------------------------------------------------------------------- @@ -3602,47 +3833,47 @@ bfloat16 QEMU_FLATTEN bfloat16_sqrt(bfloat16 a, float_status *status) float16 float16_default_nan(float_status *status) { - FloatParts p = parts_default_nan(status); + FloatParts64 p; + + parts_default_nan(&p, status); p.frac >>= float16_params.frac_shift; - return float16_pack_raw(p); + return float16_pack_raw(&p); } float32 float32_default_nan(float_status *status) { - FloatParts p = parts_default_nan(status); + FloatParts64 p; + + parts_default_nan(&p, status); p.frac >>= float32_params.frac_shift; - return float32_pack_raw(p); + return float32_pack_raw(&p); } float64 float64_default_nan(float_status *status) { - FloatParts p = parts_default_nan(status); + FloatParts64 p; + + parts_default_nan(&p, status); p.frac >>= float64_params.frac_shift; - return float64_pack_raw(p); + return float64_pack_raw(&p); } float128 float128_default_nan(float_status *status) { - FloatParts p = parts_default_nan(status); - float128 r; + FloatParts128 p; - /* Extrapolate from the choices made by parts_default_nan to fill - * in the quad-floating format. If the low bit is set, assume we - * want to set all non-snan bits. - */ - r.low = -(p.frac & 1); - r.high = p.frac >> (DECOMPOSED_BINARY_POINT - 48); - r.high |= UINT64_C(0x7FFF000000000000); - r.high |= (uint64_t)p.sign << 63; - - return r; + parts_default_nan(&p, status); + frac_shr(&p, float128_params.frac_shift); + return float128_pack_raw(&p); } bfloat16 bfloat16_default_nan(float_status *status) { - FloatParts p = parts_default_nan(status); + FloatParts64 p; + + parts_default_nan(&p, status); p.frac >>= bfloat16_params.frac_shift; - return bfloat16_pack_raw(p); + return bfloat16_pack_raw(&p); } /*---------------------------------------------------------------------------- @@ -3651,38 +3882,57 @@ bfloat16 bfloat16_default_nan(float_status *status) float16 float16_silence_nan(float16 a, float_status *status) { - FloatParts p = float16_unpack_raw(a); + FloatParts64 p; + + float16_unpack_raw(&p, a); p.frac <<= float16_params.frac_shift; - p = parts_silence_nan(p, status); + parts_silence_nan(&p, status); p.frac >>= float16_params.frac_shift; - return float16_pack_raw(p); + return float16_pack_raw(&p); } float32 float32_silence_nan(float32 a, float_status *status) { - FloatParts p = float32_unpack_raw(a); + FloatParts64 p; + + float32_unpack_raw(&p, a); p.frac <<= float32_params.frac_shift; - p = parts_silence_nan(p, status); + parts_silence_nan(&p, status); p.frac >>= float32_params.frac_shift; - return float32_pack_raw(p); + return float32_pack_raw(&p); } float64 float64_silence_nan(float64 a, float_status *status) { - FloatParts p = float64_unpack_raw(a); + FloatParts64 p; + + float64_unpack_raw(&p, a); p.frac <<= float64_params.frac_shift; - p = parts_silence_nan(p, status); + parts_silence_nan(&p, status); p.frac >>= float64_params.frac_shift; - return float64_pack_raw(p); + return float64_pack_raw(&p); } bfloat16 bfloat16_silence_nan(bfloat16 a, float_status *status) { - FloatParts p = bfloat16_unpack_raw(a); + FloatParts64 p; + + bfloat16_unpack_raw(&p, a); p.frac <<= bfloat16_params.frac_shift; - p = parts_silence_nan(p, status); + parts_silence_nan(&p, status); p.frac >>= bfloat16_params.frac_shift; - return bfloat16_pack_raw(p); + return bfloat16_pack_raw(&p); +} + +float128 float128_silence_nan(float128 a, float_status *status) +{ + FloatParts128 p; + + float128_unpack_raw(&p, a); + frac_shl(&p, float128_params.frac_shift); + parts_silence_nan(&p, status); + frac_shr(&p, float128_params.frac_shift); + return float128_pack_raw(&p); } /*---------------------------------------------------------------------------- @@ -3690,7 +3940,7 @@ bfloat16 bfloat16_silence_nan(bfloat16 a, float_status *status) | input-denormal exception and return zero. Otherwise just return the value. *----------------------------------------------------------------------------*/ -static bool parts_squash_denormal(FloatParts p, float_status *status) +static bool parts_squash_denormal(FloatParts64 p, float_status *status) { if (p.exp == 0 && p.frac != 0) { float_raise(float_flag_input_denormal, status); @@ -3703,7 +3953,9 @@ static bool parts_squash_denormal(FloatParts p, float_status *status) float16 float16_squash_input_denormal(float16 a, float_status *status) { if (status->flush_inputs_to_zero) { - FloatParts p = float16_unpack_raw(a); + FloatParts64 p; + + float16_unpack_raw(&p, a); if (parts_squash_denormal(p, status)) { return float16_set_sign(float16_zero, p.sign); } @@ -3714,7 +3966,9 @@ float16 float16_squash_input_denormal(float16 a, float_status *status) float32 float32_squash_input_denormal(float32 a, float_status *status) { if (status->flush_inputs_to_zero) { - FloatParts p = float32_unpack_raw(a); + FloatParts64 p; + + float32_unpack_raw(&p, a); if (parts_squash_denormal(p, status)) { return float32_set_sign(float32_zero, p.sign); } @@ -3725,7 +3979,9 @@ float32 float32_squash_input_denormal(float32 a, float_status *status) float64 float64_squash_input_denormal(float64 a, float_status *status) { if (status->flush_inputs_to_zero) { - FloatParts p = float64_unpack_raw(a); + FloatParts64 p; + + float64_unpack_raw(&p, a); if (parts_squash_denormal(p, status)) { return float64_set_sign(float64_zero, p.sign); } @@ -3736,7 +3992,9 @@ float64 float64_squash_input_denormal(float64 a, float_status *status) bfloat16 bfloat16_squash_input_denormal(bfloat16 a, float_status *status) { if (status->flush_inputs_to_zero) { - FloatParts p = bfloat16_unpack_raw(a); + FloatParts64 p; + + bfloat16_unpack_raw(&p, a); if (parts_squash_denormal(p, status)) { return bfloat16_set_sign(bfloat16_zero, p.sign); } @@ -3797,7 +4055,7 @@ static int32_t roundAndPackInt32(bool zSign, uint64_t absZ, return zSign ? INT32_MIN : INT32_MAX; } if (roundBits) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return z; @@ -3859,7 +4117,7 @@ static int64_t roundAndPackInt64(bool zSign, uint64_t absZ0, uint64_t absZ1, return zSign ? INT64_MIN : INT64_MAX; } if (absZ1) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return z; @@ -3920,7 +4178,7 @@ static int64_t roundAndPackUint64(bool zSign, uint64_t absZ0, } if (absZ1) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return absZ0; } @@ -4031,7 +4289,7 @@ static float32 roundAndPackFloat32(bool zSign, int zExp, uint32_t zSig, } } if (roundBits) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } zSig = ( zSig + roundIncrement )>>7; if (!(roundBits ^ 0x40) && roundNearestEven) { @@ -4187,7 +4445,7 @@ static float64 roundAndPackFloat64(bool zSign, int zExp, uint64_t zSig, } } if (roundBits) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } zSig = ( zSig + roundIncrement )>>10; if (!(roundBits ^ 0x200) && roundNearestEven) { @@ -4321,7 +4579,7 @@ floatx80 roundAndPackFloatx80(int8_t roundingPrecision, bool zSign, float_raise(float_flag_underflow, status); } if (roundBits) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } zSig0 += roundIncrement; if ( (int64_t) zSig0 < 0 ) zExp = 1; @@ -4334,7 +4592,7 @@ floatx80 roundAndPackFloatx80(int8_t roundingPrecision, bool zSign, } } if (roundBits) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } zSig0 += roundIncrement; if ( zSig0 < roundIncrement ) { @@ -4397,7 +4655,7 @@ floatx80 roundAndPackFloatx80(int8_t roundingPrecision, bool zSign, float_raise(float_flag_underflow, status); } if (zSig1) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } switch (roundingMode) { case float_round_nearest_even: @@ -4427,7 +4685,7 @@ floatx80 roundAndPackFloatx80(int8_t roundingPrecision, bool zSign, } } if (zSig1) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } if ( increment ) { ++zSig0; @@ -4704,7 +4962,7 @@ static float128 roundAndPackFloat128(bool zSign, int32_t zExp, } } if (zSig2) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } if ( increment ) { add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 ); @@ -4906,38 +5164,6 @@ floatx80 float32_to_floatx80(float32 a, float_status *status) } /*---------------------------------------------------------------------------- -| Returns the result of converting the single-precision floating-point value -| `a' to the double-precision floating-point format. The conversion is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float32_to_float128(float32 a, float_status *status) -{ - bool aSign; - int aExp; - uint32_t aSig; - - a = float32_squash_input_denormal(a, status); - aSig = extractFloat32Frac( a ); - aExp = extractFloat32Exp( a ); - aSign = extractFloat32Sign( a ); - if ( aExp == 0xFF ) { - if (aSig) { - return commonNaNToFloat128(float32ToCommonNaN(a, status), status); - } - return packFloat128( aSign, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 ); - normalizeFloat32Subnormal( aSig, &aExp, &aSig ); - --aExp; - } - return packFloat128( aSign, aExp + 0x3F80, ( (uint64_t) aSig )<<25, 0 ); - -} - -/*---------------------------------------------------------------------------- | Returns the remainder of the single-precision floating-point value `a' | with respect to the corresponding value `b'. The operation is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. @@ -5211,40 +5437,6 @@ floatx80 float64_to_floatx80(float64 a, float_status *status) } /*---------------------------------------------------------------------------- -| Returns the result of converting the double-precision floating-point value -| `a' to the quadruple-precision floating-point format. The conversion is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float64_to_float128(float64 a, float_status *status) -{ - bool aSign; - int aExp; - uint64_t aSig, zSig0, zSig1; - - a = float64_squash_input_denormal(a, status); - aSig = extractFloat64Frac( a ); - aExp = extractFloat64Exp( a ); - aSign = extractFloat64Sign( a ); - if ( aExp == 0x7FF ) { - if (aSig) { - return commonNaNToFloat128(float64ToCommonNaN(a, status), status); - } - return packFloat128( aSign, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 ); - normalizeFloat64Subnormal( aSig, &aExp, &aSig ); - --aExp; - } - shift128Right( aSig, 0, 4, &zSig0, &zSig1 ); - return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 ); - -} - - -/*---------------------------------------------------------------------------- | Returns the remainder of the double-precision floating-point value `a' | with respect to the corresponding value `b'. The operation is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. @@ -5442,7 +5634,7 @@ int32_t floatx80_to_int32_round_to_zero(floatx80 a, float_status *status) } else if ( aExp < 0x3FFF ) { if (aExp || aSig) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return 0; } @@ -5457,7 +5649,7 @@ int32_t floatx80_to_int32_round_to_zero(floatx80 a, float_status *status) return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<<shiftCount ) != savedASig ) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return z; @@ -5541,13 +5733,13 @@ int64_t floatx80_to_int64_round_to_zero(floatx80 a, float_status *status) } else if ( aExp < 0x3FFF ) { if (aExp | aSig) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return 0; } z = aSig>>( - shiftCount ); if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } if ( aSign ) z = - z; return z; @@ -5698,7 +5890,7 @@ floatx80 floatx80_round_to_int(floatx80 a, float_status *status) && ( (uint64_t) ( extractFloatx80Frac( a ) ) == 0 ) ) { return a; } - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); aSign = extractFloatx80Sign( a ); switch (status->float_rounding_mode) { case float_round_nearest_even: @@ -5765,7 +5957,7 @@ floatx80 floatx80_round_to_int(floatx80 a, float_status *status) z.low = UINT64_C(0x8000000000000000); } if (z.low != a.low) { - status->float_exception_flags |= float_flag_inexact; + float_raise(float_flag_inexact, status); } return z; @@ -6345,191 +6537,6 @@ floatx80 floatx80_sqrt(floatx80 a, float_status *status) } /*---------------------------------------------------------------------------- -| Returns the result of converting the quadruple-precision floating-point -| value `a' to the 32-bit two's complement integer format. The conversion -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic---which means in particular that the conversion is rounded -| according to the current rounding mode. If `a' is a NaN, the largest -| positive integer is returned. Otherwise, if the conversion overflows, the -| largest integer with the same sign as `a' is returned. -*----------------------------------------------------------------------------*/ - -int32_t float128_to_int32(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp, shiftCount; - uint64_t aSig0, aSig1; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0; - if ( aExp ) aSig0 |= UINT64_C(0x0001000000000000); - aSig0 |= ( aSig1 != 0 ); - shiftCount = 0x4028 - aExp; - if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 ); - return roundAndPackInt32(aSign, aSig0, status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the quadruple-precision floating-point -| value `a' to the 32-bit two's complement integer format. The conversion -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic, except that the conversion is always rounded toward zero. If -| `a' is a NaN, the largest positive integer is returned. Otherwise, if the -| conversion overflows, the largest integer with the same sign as `a' is -| returned. -*----------------------------------------------------------------------------*/ - -int32_t float128_to_int32_round_to_zero(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp, shiftCount; - uint64_t aSig0, aSig1, savedASig; - int32_t z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - aSig0 |= ( aSig1 != 0 ); - if ( 0x401E < aExp ) { - if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0; - goto invalid; - } - else if ( aExp < 0x3FFF ) { - if (aExp || aSig0) { - status->float_exception_flags |= float_flag_inexact; - } - return 0; - } - aSig0 |= UINT64_C(0x0001000000000000); - shiftCount = 0x402F - aExp; - savedASig = aSig0; - aSig0 >>= shiftCount; - z = aSig0; - if ( aSign ) z = - z; - if ( ( z < 0 ) ^ aSign ) { - invalid: - float_raise(float_flag_invalid, status); - return aSign ? INT32_MIN : INT32_MAX; - } - if ( ( aSig0<<shiftCount ) != savedASig ) { - status->float_exception_flags |= float_flag_inexact; - } - return z; - -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the quadruple-precision floating-point -| value `a' to the 64-bit two's complement integer format. The conversion -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic---which means in particular that the conversion is rounded -| according to the current rounding mode. If `a' is a NaN, the largest -| positive integer is returned. Otherwise, if the conversion overflows, the -| largest integer with the same sign as `a' is returned. -*----------------------------------------------------------------------------*/ - -int64_t float128_to_int64(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp, shiftCount; - uint64_t aSig0, aSig1; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - if ( aExp ) aSig0 |= UINT64_C(0x0001000000000000); - shiftCount = 0x402F - aExp; - if ( shiftCount <= 0 ) { - if ( 0x403E < aExp ) { - float_raise(float_flag_invalid, status); - if ( ! aSign - || ( ( aExp == 0x7FFF ) - && ( aSig1 || ( aSig0 != UINT64_C(0x0001000000000000) ) ) - ) - ) { - return INT64_MAX; - } - return INT64_MIN; - } - shortShift128Left( aSig0, aSig1, - shiftCount, &aSig0, &aSig1 ); - } - else { - shift64ExtraRightJamming( aSig0, aSig1, shiftCount, &aSig0, &aSig1 ); - } - return roundAndPackInt64(aSign, aSig0, aSig1, status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the quadruple-precision floating-point -| value `a' to the 64-bit two's complement integer format. The conversion -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic, except that the conversion is always rounded toward zero. -| If `a' is a NaN, the largest positive integer is returned. Otherwise, if -| the conversion overflows, the largest integer with the same sign as `a' is -| returned. -*----------------------------------------------------------------------------*/ - -int64_t float128_to_int64_round_to_zero(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp, shiftCount; - uint64_t aSig0, aSig1; - int64_t z; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - if ( aExp ) aSig0 |= UINT64_C(0x0001000000000000); - shiftCount = aExp - 0x402F; - if ( 0 < shiftCount ) { - if ( 0x403E <= aExp ) { - aSig0 &= UINT64_C(0x0000FFFFFFFFFFFF); - if ( ( a.high == UINT64_C(0xC03E000000000000) ) - && ( aSig1 < UINT64_C(0x0002000000000000) ) ) { - if (aSig1) { - status->float_exception_flags |= float_flag_inexact; - } - } - else { - float_raise(float_flag_invalid, status); - if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) ) { - return INT64_MAX; - } - } - return INT64_MIN; - } - z = ( aSig0<<shiftCount ) | ( aSig1>>( ( - shiftCount ) & 63 ) ); - if ( (uint64_t) ( aSig1<<shiftCount ) ) { - status->float_exception_flags |= float_flag_inexact; - } - } - else { - if ( aExp < 0x3FFF ) { - if ( aExp | aSig0 | aSig1 ) { - status->float_exception_flags |= float_flag_inexact; - } - return 0; - } - z = aSig0>>( - shiftCount ); - if ( aSig1 - || ( shiftCount && (uint64_t) ( aSig0<<( shiftCount & 63 ) ) ) ) { - status->float_exception_flags |= float_flag_inexact; - } - } - if ( aSign ) z = - z; - return z; - -} - -/*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point value | `a' to the 64-bit unsigned integer format. The conversion is | performed according to the IEC/IEEE Standard for Binary Floating-Point @@ -6647,74 +6654,6 @@ uint32_t float128_to_uint32(float128 a, float_status *status) /*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point -| value `a' to the single-precision floating-point format. The conversion -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float32 float128_to_float32(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp; - uint64_t aSig0, aSig1; - uint32_t zSig; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) { - return commonNaNToFloat32(float128ToCommonNaN(a, status), status); - } - return packFloat32( aSign, 0xFF, 0 ); - } - aSig0 |= ( aSig1 != 0 ); - shift64RightJamming( aSig0, 18, &aSig0 ); - zSig = aSig0; - if ( aExp || zSig ) { - zSig |= 0x40000000; - aExp -= 0x3F81; - } - return roundAndPackFloat32(aSign, aExp, zSig, status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the quadruple-precision floating-point -| value `a' to the double-precision floating-point format. The conversion -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float64 float128_to_float64(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp; - uint64_t aSig0, aSig1; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) { - return commonNaNToFloat64(float128ToCommonNaN(a, status), status); - } - return packFloat64( aSign, 0x7FF, 0 ); - } - shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 ); - aSig0 |= ( aSig1 != 0 ); - if ( aExp || aSig0 ) { - aSig0 |= UINT64_C(0x4000000000000000); - aExp -= 0x3C01; - } - return roundAndPackFloat64(aSign, aExp, aSig0, status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the quadruple-precision floating-point | value `a' to the extended double-precision floating-point format. The | conversion is performed according to the IEC/IEEE Standard for Binary | Floating-Point Arithmetic. @@ -6752,536 +6691,6 @@ floatx80 float128_to_floatx80(float128 a, float_status *status) } /*---------------------------------------------------------------------------- -| Rounds the quadruple-precision floating-point value `a' to an integer, and -| returns the result as a quadruple-precision floating-point value. The -| operation is performed according to the IEC/IEEE Standard for Binary -| Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float128_round_to_int(float128 a, float_status *status) -{ - bool aSign; - int32_t aExp; - uint64_t lastBitMask, roundBitsMask; - float128 z; - - aExp = extractFloat128Exp( a ); - if ( 0x402F <= aExp ) { - if ( 0x406F <= aExp ) { - if ( ( aExp == 0x7FFF ) - && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) - ) { - return propagateFloat128NaN(a, a, status); - } - return a; - } - lastBitMask = 1; - lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1; - roundBitsMask = lastBitMask - 1; - z = a; - switch (status->float_rounding_mode) { - case float_round_nearest_even: - if ( lastBitMask ) { - add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low ); - if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; - } - else { - if ( (int64_t) z.low < 0 ) { - ++z.high; - if ( (uint64_t) ( z.low<<1 ) == 0 ) z.high &= ~1; - } - } - break; - case float_round_ties_away: - if (lastBitMask) { - add128(z.high, z.low, 0, lastBitMask >> 1, &z.high, &z.low); - } else { - if ((int64_t) z.low < 0) { - ++z.high; - } - } - break; - case float_round_to_zero: - break; - case float_round_up: - if (!extractFloat128Sign(z)) { - add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low); - } - break; - case float_round_down: - if (extractFloat128Sign(z)) { - add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low); - } - break; - case float_round_to_odd: - /* - * Note that if lastBitMask == 0, the last bit is the lsb - * of high, and roundBitsMask == -1. - */ - if ((lastBitMask ? z.low & lastBitMask : z.high & 1) == 0) { - add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low); - } - break; - default: - abort(); - } - z.low &= ~ roundBitsMask; - } - else { - if ( aExp < 0x3FFF ) { - if ( ( ( (uint64_t) ( a.high<<1 ) ) | a.low ) == 0 ) return a; - status->float_exception_flags |= float_flag_inexact; - aSign = extractFloat128Sign( a ); - switch (status->float_rounding_mode) { - case float_round_nearest_even: - if ( ( aExp == 0x3FFE ) - && ( extractFloat128Frac0( a ) - | extractFloat128Frac1( a ) ) - ) { - return packFloat128( aSign, 0x3FFF, 0, 0 ); - } - break; - case float_round_ties_away: - if (aExp == 0x3FFE) { - return packFloat128(aSign, 0x3FFF, 0, 0); - } - break; - case float_round_down: - return - aSign ? packFloat128( 1, 0x3FFF, 0, 0 ) - : packFloat128( 0, 0, 0, 0 ); - case float_round_up: - return - aSign ? packFloat128( 1, 0, 0, 0 ) - : packFloat128( 0, 0x3FFF, 0, 0 ); - - case float_round_to_odd: - return packFloat128(aSign, 0x3FFF, 0, 0); - - case float_round_to_zero: - break; - } - return packFloat128( aSign, 0, 0, 0 ); - } - lastBitMask = 1; - lastBitMask <<= 0x402F - aExp; - roundBitsMask = lastBitMask - 1; - z.low = 0; - z.high = a.high; - switch (status->float_rounding_mode) { - case float_round_nearest_even: - z.high += lastBitMask>>1; - if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) { - z.high &= ~ lastBitMask; - } - break; - case float_round_ties_away: - z.high += lastBitMask>>1; - break; - case float_round_to_zero: - break; - case float_round_up: - if (!extractFloat128Sign(z)) { - z.high |= ( a.low != 0 ); - z.high += roundBitsMask; - } - break; - case float_round_down: - if (extractFloat128Sign(z)) { - z.high |= (a.low != 0); - z.high += roundBitsMask; - } - break; - case float_round_to_odd: - if ((z.high & lastBitMask) == 0) { - z.high |= (a.low != 0); - z.high += roundBitsMask; - } - break; - default: - abort(); - } - z.high &= ~ roundBitsMask; - } - if ( ( z.low != a.low ) || ( z.high != a.high ) ) { - status->float_exception_flags |= float_flag_inexact; - } - return z; - -} - -/*---------------------------------------------------------------------------- -| Returns the result of adding the absolute values of the quadruple-precision -| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated -| before being returned. `zSign' is ignored if the result is a NaN. -| The addition is performed according to the IEC/IEEE Standard for Binary -| Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -static float128 addFloat128Sigs(float128 a, float128 b, bool zSign, - float_status *status) -{ - int32_t aExp, bExp, zExp; - uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - int32_t expDiff; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - expDiff = aExp - bExp; - if ( 0 < expDiff ) { - if ( aExp == 0x7FFF ) { - if (aSig0 | aSig1) { - return propagateFloat128NaN(a, b, status); - } - return a; - } - if ( bExp == 0 ) { - --expDiff; - } - else { - bSig0 |= UINT64_C(0x0001000000000000); - } - shift128ExtraRightJamming( - bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 ); - zExp = aExp; - } - else if ( expDiff < 0 ) { - if ( bExp == 0x7FFF ) { - if (bSig0 | bSig1) { - return propagateFloat128NaN(a, b, status); - } - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - ++expDiff; - } - else { - aSig0 |= UINT64_C(0x0001000000000000); - } - shift128ExtraRightJamming( - aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 ); - zExp = bExp; - } - else { - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 | bSig0 | bSig1 ) { - return propagateFloat128NaN(a, b, status); - } - return a; - } - add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); - if ( aExp == 0 ) { - if (status->flush_to_zero) { - if (zSig0 | zSig1) { - float_raise(float_flag_output_denormal, status); - } - return packFloat128(zSign, 0, 0, 0); - } - return packFloat128( zSign, 0, zSig0, zSig1 ); - } - zSig2 = 0; - zSig0 |= UINT64_C(0x0002000000000000); - zExp = aExp; - goto shiftRight1; - } - aSig0 |= UINT64_C(0x0001000000000000); - add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); - --zExp; - if ( zSig0 < UINT64_C(0x0002000000000000) ) goto roundAndPack; - ++zExp; - shiftRight1: - shift128ExtraRightJamming( - zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); - roundAndPack: - return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of subtracting the absolute values of the quadruple- -| precision floating-point values `a' and `b'. If `zSign' is 1, the -| difference is negated before being returned. `zSign' is ignored if the -| result is a NaN. The subtraction is performed according to the IEC/IEEE -| Standard for Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -static float128 subFloat128Sigs(float128 a, float128 b, bool zSign, - float_status *status) -{ - int32_t aExp, bExp, zExp; - uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; - int32_t expDiff; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - expDiff = aExp - bExp; - shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 ); - shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 ); - if ( 0 < expDiff ) goto aExpBigger; - if ( expDiff < 0 ) goto bExpBigger; - if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 | bSig0 | bSig1 ) { - return propagateFloat128NaN(a, b, status); - } - float_raise(float_flag_invalid, status); - return float128_default_nan(status); - } - if ( aExp == 0 ) { - aExp = 1; - bExp = 1; - } - if ( bSig0 < aSig0 ) goto aBigger; - if ( aSig0 < bSig0 ) goto bBigger; - if ( bSig1 < aSig1 ) goto aBigger; - if ( aSig1 < bSig1 ) goto bBigger; - return packFloat128(status->float_rounding_mode == float_round_down, - 0, 0, 0); - bExpBigger: - if ( bExp == 0x7FFF ) { - if (bSig0 | bSig1) { - return propagateFloat128NaN(a, b, status); - } - return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - ++expDiff; - } - else { - aSig0 |= UINT64_C(0x4000000000000000); - } - shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); - bSig0 |= UINT64_C(0x4000000000000000); - bBigger: - sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 ); - zExp = bExp; - zSign ^= 1; - goto normalizeRoundAndPack; - aExpBigger: - if ( aExp == 0x7FFF ) { - if (aSig0 | aSig1) { - return propagateFloat128NaN(a, b, status); - } - return a; - } - if ( bExp == 0 ) { - --expDiff; - } - else { - bSig0 |= UINT64_C(0x4000000000000000); - } - shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 ); - aSig0 |= UINT64_C(0x4000000000000000); - aBigger: - sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); - zExp = aExp; - normalizeRoundAndPack: - --zExp; - return normalizeRoundAndPackFloat128(zSign, zExp - 14, zSig0, zSig1, - status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of adding the quadruple-precision floating-point values -| `a' and `b'. The operation is performed according to the IEC/IEEE Standard -| for Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float128_add(float128 a, float128 b, float_status *status) -{ - bool aSign, bSign; - - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign == bSign ) { - return addFloat128Sigs(a, b, aSign, status); - } - else { - return subFloat128Sigs(a, b, aSign, status); - } - -} - -/*---------------------------------------------------------------------------- -| Returns the result of subtracting the quadruple-precision floating-point -| values `a' and `b'. The operation is performed according to the IEC/IEEE -| Standard for Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float128_sub(float128 a, float128 b, float_status *status) -{ - bool aSign, bSign; - - aSign = extractFloat128Sign( a ); - bSign = extractFloat128Sign( b ); - if ( aSign == bSign ) { - return subFloat128Sigs(a, b, aSign, status); - } - else { - return addFloat128Sigs(a, b, aSign, status); - } - -} - -/*---------------------------------------------------------------------------- -| Returns the result of multiplying the quadruple-precision floating-point -| values `a' and `b'. The operation is performed according to the IEC/IEEE -| Standard for Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float128_mul(float128 a, float128 b, float_status *status) -{ - bool aSign, bSign, zSign; - int32_t aExp, bExp, zExp; - uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - bSign = extractFloat128Sign( b ); - zSign = aSign ^ bSign; - if ( aExp == 0x7FFF ) { - if ( ( aSig0 | aSig1 ) - || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { - return propagateFloat128NaN(a, b, status); - } - if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid; - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( bExp == 0x7FFF ) { - if (bSig0 | bSig1) { - return propagateFloat128NaN(a, b, status); - } - if ( ( aExp | aSig0 | aSig1 ) == 0 ) { - invalid: - float_raise(float_flag_invalid, status); - return float128_default_nan(status); - } - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( aExp == 0 ) { - if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); - normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); - } - if ( bExp == 0 ) { - if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); - normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); - } - zExp = aExp + bExp - 0x4000; - aSig0 |= UINT64_C(0x0001000000000000); - shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 ); - mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 ); - add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 ); - zSig2 |= ( zSig3 != 0 ); - if (UINT64_C( 0x0002000000000000) <= zSig0 ) { - shift128ExtraRightJamming( - zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); - ++zExp; - } - return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); - -} - -/*---------------------------------------------------------------------------- -| Returns the result of dividing the quadruple-precision floating-point value -| `a' by the corresponding value `b'. The operation is performed according to -| the IEC/IEEE Standard for Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -float128 float128_div(float128 a, float128 b, float_status *status) -{ - bool aSign, bSign, zSign; - int32_t aExp, bExp, zExp; - uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; - - aSig1 = extractFloat128Frac1( a ); - aSig0 = extractFloat128Frac0( a ); - aExp = extractFloat128Exp( a ); - aSign = extractFloat128Sign( a ); - bSig1 = extractFloat128Frac1( b ); - bSig0 = extractFloat128Frac0( b ); - bExp = extractFloat128Exp( b ); - bSign = extractFloat128Sign( b ); - zSign = aSign ^ bSign; - if ( aExp == 0x7FFF ) { - if (aSig0 | aSig1) { - return propagateFloat128NaN(a, b, status); - } - if ( bExp == 0x7FFF ) { - if (bSig0 | bSig1) { - return propagateFloat128NaN(a, b, status); - } - goto invalid; - } - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - if ( bExp == 0x7FFF ) { - if (bSig0 | bSig1) { - return propagateFloat128NaN(a, b, status); - } - return packFloat128( zSign, 0, 0, 0 ); - } - if ( bExp == 0 ) { - if ( ( bSig0 | bSig1 ) == 0 ) { - if ( ( aExp | aSig0 | aSig1 ) == 0 ) { - invalid: - float_raise(float_flag_invalid, status); - return float128_default_nan(status); - } - float_raise(float_flag_divbyzero, status); - return packFloat128( zSign, 0x7FFF, 0, 0 ); - } - normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); - } - if ( aExp == 0 ) { - if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); - normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); - } - zExp = aExp - bExp + 0x3FFD; - shortShift128Left( - aSig0 | UINT64_C(0x0001000000000000), aSig1, 15, &aSig0, &aSig1 ); - shortShift128Left( - bSig0 | UINT64_C(0x0001000000000000), bSig1, 15, &bSig0, &bSig1 ); - if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) { - shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 ); - ++zExp; - } - zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 ); - mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 ); - sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 ); - while ( (int64_t) rem0 < 0 ) { - --zSig0; - add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 ); - } - zSig1 = estimateDiv128To64( rem1, rem2, bSig0 ); - if ( ( zSig1 & 0x3FFF ) <= 4 ) { - mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 ); - sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 ); - while ( (int64_t) rem1 < 0 ) { - --zSig1; - add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 ); - } - zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); - } - shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); - return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); - -} - -/*---------------------------------------------------------------------------- | Returns the remainder of the quadruple-precision floating-point value `a' | with respect to the corresponding value `b'. The operation is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index d33ce8954a..f0035d2b4a 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1830,6 +1830,7 @@ build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, int i; unsigned rsdt_entries_offset; AcpiRsdtDescriptorRev1 *rsdt; + int rsdt_start = table_data->len; const unsigned table_data_len = (sizeof(uint32_t) * table_offsets->len); const unsigned rsdt_entry_size = sizeof(rsdt->table_offset_entry[0]); const size_t rsdt_len = sizeof(*rsdt) + table_data_len; @@ -1846,7 +1847,8 @@ build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, ACPI_BUILD_TABLE_FILE, ref_tbl_offset); } build_header(linker, table_data, - (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id); + (void *)(table_data->data + rsdt_start), + "RSDT", rsdt_len, 1, oem_id, oem_table_id); } /* Build xsdt table */ @@ -1857,6 +1859,7 @@ build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, int i; unsigned xsdt_entries_offset; AcpiXsdtDescriptorRev2 *xsdt; + int xsdt_start = table_data->len; const unsigned table_data_len = (sizeof(uint64_t) * table_offsets->len); const unsigned xsdt_entry_size = sizeof(xsdt->table_offset_entry[0]); const size_t xsdt_len = sizeof(*xsdt) + table_data_len; @@ -1873,7 +1876,8 @@ build_xsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, ACPI_BUILD_TABLE_FILE, ref_tbl_offset); } build_header(linker, table_data, - (void *)xsdt, "XSDT", xsdt_len, 1, oem_id, oem_table_id); + (void *)(table_data->data + xsdt_start), + "XSDT", xsdt_len, 1, oem_id, oem_table_id); } void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, @@ -2053,10 +2057,9 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, uint64_t control_area_start_address; TPMIf *tpmif = tpm_find(); uint32_t start_method; - void *tpm2_ptr; tpm2_start = table_data->len; - tpm2_ptr = acpi_data_push(table_data, sizeof(AcpiTableHeader)); + acpi_data_push(table_data, sizeof(AcpiTableHeader)); /* Platform Class */ build_append_int_noprefix(table_data, TPM2_ACPI_CLASS_CLIENT, 2); @@ -2095,8 +2098,8 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, log_addr_offset, 8, ACPI_BUILD_TPMLOG_FILE, 0); build_header(linker, table_data, - tpm2_ptr, "TPM2", table_data->len - tpm2_start, 4, oem_id, - oem_table_id); + (void *)(table_data->data + tpm2_start), + "TPM2", table_data->len - tpm2_start, 4, oem_id, oem_table_id); } Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set, uint32_t io_offset, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0a78532018..840758666d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -50,6 +50,7 @@ #include "sysemu/tpm.h" #include "sysemu/kvm.h" #include "hw/loader.h" +#include "qapi/error.h" #include "qemu/bitops.h" #include "qemu/error-report.h" #include "qemu/module.h" @@ -1521,8 +1522,10 @@ static void virt_build_smbios(VirtMachineState *vms) vmc->smbios_old_sys_ver ? "1.0" : mc->name, false, true, SMBIOS_ENTRY_POINT_30); - smbios_get_tables(MACHINE(vms), NULL, 0, &smbios_tables, &smbios_tables_len, - &smbios_anchor, &smbios_anchor_len); + smbios_get_tables(MACHINE(vms), NULL, 0, + &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len, + &error_fatal); if (smbios_anchor) { fw_cfg_add_file(vms->fw_cfg, "etc/smbios/smbios-tables", diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index 80b8a41cb5..0608e2d475 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -401,7 +401,7 @@ static void atmega1280_class_init(ObjectClass *oc, void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); - amc->cpu_type = AVR_CPU_TYPE_NAME("avr6"); + amc->cpu_type = AVR_CPU_TYPE_NAME("avr51"); amc->flash_size = 128 * KiB; amc->eeprom_size = 4 * KiB; amc->sram_size = 8 * KiB; diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index e9050c8987..cd81893d1d 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -198,19 +198,30 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) goto fail_guest_notifiers; } + memory_region_transaction_begin(); + /* Set up virtqueue notify */ for (i = 0; i < nvqs; i++) { r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true); if (r != 0) { + int j = i; + fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); while (i--) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + memory_region_transaction_commit(); + + while (j--) { virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); } - goto fail_guest_notifiers; + goto fail_host_notifiers; } } + memory_region_transaction_commit(); + s->starting = false; vblk->dataplane_started = true; trace_virtio_blk_data_plane_start(s); @@ -221,7 +232,7 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) aio_context_release(old_context); if (r < 0) { error_report_err(local_err); - goto fail_guest_notifiers; + goto fail_aio_context; } /* Process queued requests before the ones in vring */ @@ -245,6 +256,20 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) aio_context_release(s->ctx); return 0; + fail_aio_context: + memory_region_transaction_begin(); + + for (i = 0; i < nvqs; i++) { + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + memory_region_transaction_commit(); + + for (i = 0; i < nvqs; i++) { + virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); + } + fail_host_notifiers: + k->set_guest_notifiers(qbus->parent, nvqs, false); fail_guest_notifiers: /* * If we failed to set up the guest notifiers queued requests will be @@ -305,8 +330,15 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) aio_context_release(s->ctx); + memory_region_transaction_begin(); + for (i = 0; i < nvqs; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + memory_region_transaction_commit(); + + for (i = 0; i < nvqs; i++) { virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); } diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index d28979efb8..f139cd7cc9 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -40,7 +40,7 @@ * Starting from the discard feature, we can use this array to properly * set the config size depending on the features enabled. */ -static VirtIOFeature feature_sizes[] = { +static const VirtIOFeature feature_sizes[] = { {.flags = 1ULL << VIRTIO_BLK_F_DISCARD, .end = endof(struct virtio_blk_config, discard_sector_alignment)}, {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES, diff --git a/hw/core/numa.c b/hw/core/numa.c index ac6bed5817..1058d3697b 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -802,9 +802,27 @@ void query_numa_node_mem(NumaNodeMem node_mem[], MachineState *ms) } } +static int ram_block_notify_add_single(RAMBlock *rb, void *opaque) +{ + const ram_addr_t max_size = qemu_ram_get_max_length(rb); + const ram_addr_t size = qemu_ram_get_used_length(rb); + void *host = qemu_ram_get_host_addr(rb); + RAMBlockNotifier *notifier = opaque; + + if (host) { + notifier->ram_block_added(notifier, host, size, max_size); + } + return 0; +} + void ram_block_notifier_add(RAMBlockNotifier *n) { QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next); + + /* Notify about all existing ram blocks. */ + if (n->ram_block_added) { + qemu_ram_foreach_block(ram_block_notify_add_single, n); + } } void ram_block_notifier_remove(RAMBlockNotifier *n) @@ -812,20 +830,35 @@ void ram_block_notifier_remove(RAMBlockNotifier *n) QLIST_REMOVE(n, next); } -void ram_block_notify_add(void *host, size_t size) +void ram_block_notify_add(void *host, size_t size, size_t max_size) +{ + RAMBlockNotifier *notifier; + + QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { + if (notifier->ram_block_added) { + notifier->ram_block_added(notifier, host, size, max_size); + } + } +} + +void ram_block_notify_remove(void *host, size_t size, size_t max_size) { RAMBlockNotifier *notifier; QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { - notifier->ram_block_added(notifier, host, size); + if (notifier->ram_block_removed) { + notifier->ram_block_removed(notifier, host, size, max_size); + } } } -void ram_block_notify_remove(void *host, size_t size) +void ram_block_notify_resize(void *host, size_t old_size, size_t new_size) { RAMBlockNotifier *notifier; QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { - notifier->ram_block_removed(notifier, host, size); + if (notifier->ram_block_resized) { + notifier->ram_block_resized(notifier, host, old_size, new_size); + } } } diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 985a259e05..34d8acb0e3 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -170,7 +170,7 @@ /* AST2600 only - 1.8V gpios */ /* * The AST2600 has same 3.6V gpios as the AST2400 (memory offsets 0x0-0x198) - * and addtional 1.8V gpios (memory offsets 0x800-0x9D4). + * and additional 1.8V gpios (memory offsets 0x800-0x9D4). */ #define GPIO_1_8V_REG_OFFSET 0x800 #define GPIO_1_8V_ABCD_DATA_VALUE ((0x800 - GPIO_1_8V_REG_OFFSET) >> 2) diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 09642a6dcb..8d120a25d5 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -28,3 +28,7 @@ config IMX_I2C config MPC_I2C bool select I2C + +config PCA954X + bool + select I2C diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 21ec52ac5a..3a7bae311d 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -77,6 +77,30 @@ int i2c_bus_busy(I2CBus *bus) return !QLIST_EMPTY(&bus->current_devs); } +bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, + I2CNodeList *current_devs) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { + DeviceState *qdev = kid->child; + I2CSlave *candidate = I2C_SLAVE(qdev); + I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(candidate); + + if (sc->match_and_add(candidate, address, broadcast, current_devs)) { + if (!broadcast) { + return true; + } + } + } + + /* + * If broadcast was true, and the list was full or empty, return true. If + * broadcast was false, return false. + */ + return broadcast; +} + /* TODO: Make this handle multiple masters. */ /* * Start or continue an i2c transaction. When this is called for the @@ -93,7 +117,6 @@ int i2c_bus_busy(I2CBus *bus) */ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) { - BusChild *kid; I2CSlaveClass *sc; I2CNode *node; bool bus_scanned = false; @@ -115,18 +138,8 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) * terminating the previous transaction. */ if (QLIST_EMPTY(&bus->current_devs)) { - QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - I2CSlave *candidate = I2C_SLAVE(qdev); - if ((candidate->address == address) || (bus->broadcast)) { - node = g_malloc(sizeof(struct I2CNode)); - node->elt = candidate; - QLIST_INSERT_HEAD(&bus->current_devs, node, next); - if (!bus->broadcast) { - break; - } - } - } + /* Disregard whether devices were found. */ + (void)i2c_scan_bus(bus, address, bus->broadcast, &bus->current_devs); bus_scanned = true; } @@ -290,12 +303,28 @@ I2CSlave *i2c_slave_create_simple(I2CBus *bus, const char *name, uint8_t addr) return dev; } +static bool i2c_slave_match(I2CSlave *candidate, uint8_t address, + bool broadcast, I2CNodeList *current_devs) +{ + if ((candidate->address == address) || (broadcast)) { + I2CNode *node = g_malloc(sizeof(struct I2CNode)); + node->elt = candidate; + QLIST_INSERT_HEAD(current_devs, node, next); + return true; + } + + /* Not found and not broadcast. */ + return false; +} + static void i2c_slave_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); set_bit(DEVICE_CATEGORY_MISC, k->categories); k->bus_type = TYPE_I2C_BUS; device_class_set_props(k, i2c_props); + sc->match_and_add = i2c_slave_match; } static const TypeInfo i2c_slave_type_info = { diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c new file mode 100644 index 0000000000..847c59921c --- /dev/null +++ b/hw/i2c/i2c_mux_pca954x.c @@ -0,0 +1,290 @@ +/* + * I2C multiplexer for PCA954x series of I2C multiplexer/switch chips. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/i2c_mux_pca954x.h" +#include "hw/i2c/smbus_slave.h" +#include "hw/qdev-core.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/queue.h" +#include "qom/object.h" +#include "trace.h" + +#define PCA9548_CHANNEL_COUNT 8 +#define PCA9546_CHANNEL_COUNT 4 + +/* + * struct Pca954xChannel - The i2c mux device will have N of these states + * that own the i2c channel bus. + * @bus: The owned channel bus. + * @enabled: Is this channel active? + */ +typedef struct Pca954xChannel { + SysBusDevice parent; + + I2CBus *bus; + + bool enabled; +} Pca954xChannel; + +#define TYPE_PCA954X_CHANNEL "pca954x-channel" +#define PCA954X_CHANNEL(obj) \ + OBJECT_CHECK(Pca954xChannel, (obj), TYPE_PCA954X_CHANNEL) + +/* + * struct Pca954xState - The pca954x state object. + * @control: The value written to the mux control. + * @channel: The set of i2c channel buses that act as channels which own the + * i2c children. + */ +typedef struct Pca954xState { + SMBusDevice parent; + + uint8_t control; + + /* The channel i2c buses. */ + Pca954xChannel channel[PCA9548_CHANNEL_COUNT]; +} Pca954xState; + +/* + * struct Pca954xClass - The pca954x class object. + * @nchans: The number of i2c channels this device has. + */ +typedef struct Pca954xClass { + SMBusDeviceClass parent; + + uint8_t nchans; +} Pca954xClass; + +#define TYPE_PCA954X "pca954x" +OBJECT_DECLARE_TYPE(Pca954xState, Pca954xClass, PCA954X) + +/* + * For each channel, if it's enabled, recursively call match on those children. + */ +static bool pca954x_match(I2CSlave *candidate, uint8_t address, + bool broadcast, + I2CNodeList *current_devs) +{ + Pca954xState *mux = PCA954X(candidate); + Pca954xClass *mc = PCA954X_GET_CLASS(mux); + int i; + + /* They are talking to the mux itself (or all devices enabled). */ + if ((candidate->address == address) || broadcast) { + I2CNode *node = g_malloc(sizeof(struct I2CNode)); + node->elt = candidate; + QLIST_INSERT_HEAD(current_devs, node, next); + if (!broadcast) { + return true; + } + } + + for (i = 0; i < mc->nchans; i++) { + if (!mux->channel[i].enabled) { + continue; + } + + if (i2c_scan_bus(mux->channel[i].bus, address, broadcast, + current_devs)) { + if (!broadcast) { + return true; + } + } + } + + /* If we arrived here we didn't find a match, return broadcast. */ + return broadcast; +} + +static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask) +{ + Pca954xClass *mc = PCA954X_GET_CLASS(s); + int i; + + /* + * For each channel, check if their bit is set in enable_mask and if yes, + * enable it, otherwise disable, hide it. + */ + for (i = 0; i < mc->nchans; i++) { + if (enable_mask & (1 << i)) { + s->channel[i].enabled = true; + } else { + s->channel[i].enabled = false; + } + } +} + +static void pca954x_write(Pca954xState *s, uint8_t data) +{ + s->control = data; + pca954x_enable_channel(s, data); + + trace_pca954x_write_bytes(data); +} + +static int pca954x_write_data(SMBusDevice *d, uint8_t *buf, uint8_t len) +{ + Pca954xState *s = PCA954X(d); + + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + /* + * len should be 1, because they write one byte to enable/disable channels. + */ + if (len > 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: extra data after channel selection mask\n", + __func__); + return -1; + } + + pca954x_write(s, buf[0]); + return 0; +} + +static uint8_t pca954x_read_byte(SMBusDevice *d) +{ + Pca954xState *s = PCA954X(d); + uint8_t data = s->control; + trace_pca954x_read_data(data); + return data; +} + +static void pca954x_enter_reset(Object *obj, ResetType type) +{ + Pca954xState *s = PCA954X(obj); + /* Reset will disable all channels. */ + pca954x_write(s, 0); +} + +I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) +{ + Pca954xClass *pc = PCA954X_GET_CLASS(mux); + Pca954xState *pca954x = PCA954X(mux); + + g_assert(channel < pc->nchans); + return I2C_BUS(qdev_get_child_bus(DEVICE(&pca954x->channel[channel]), + "i2c-bus")); +} + +static void pca954x_channel_init(Object *obj) +{ + Pca954xChannel *s = PCA954X_CHANNEL(obj); + s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); + + /* Start all channels as disabled. */ + s->enabled = false; +} + +static void pca954x_channel_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "Pca954x Channel"; +} + +static void pca9546_class_init(ObjectClass *klass, void *data) +{ + Pca954xClass *s = PCA954X_CLASS(klass); + s->nchans = PCA9546_CHANNEL_COUNT; +} + +static void pca9548_class_init(ObjectClass *klass, void *data) +{ + Pca954xClass *s = PCA954X_CLASS(klass); + s->nchans = PCA9548_CHANNEL_COUNT; +} + +static void pca954x_realize(DeviceState *dev, Error **errp) +{ + Pca954xState *s = PCA954X(dev); + Pca954xClass *c = PCA954X_GET_CLASS(s); + int i; + + /* SMBus modules. Cannot fail. */ + for (i = 0; i < c->nchans; i++) { + sysbus_realize(SYS_BUS_DEVICE(&s->channel[i]), &error_abort); + } +} + +static void pca954x_init(Object *obj) +{ + Pca954xState *s = PCA954X(obj); + Pca954xClass *c = PCA954X_GET_CLASS(obj); + int i; + + /* Only initialize the children we expect. */ + for (i = 0; i < c->nchans; i++) { + object_initialize_child(obj, "channel[*]", &s->channel[i], + TYPE_PCA954X_CHANNEL); + } +} + +static void pca954x_class_init(ObjectClass *klass, void *data) +{ + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); + + sc->match_and_add = pca954x_match; + + rc->phases.enter = pca954x_enter_reset; + + dc->desc = "Pca954x i2c-mux"; + dc->realize = pca954x_realize; + + k->write_data = pca954x_write_data; + k->receive_byte = pca954x_read_byte; +} + +static const TypeInfo pca954x_info[] = { + { + .name = TYPE_PCA954X, + .parent = TYPE_SMBUS_DEVICE, + .instance_size = sizeof(Pca954xState), + .instance_init = pca954x_init, + .class_size = sizeof(Pca954xClass), + .class_init = pca954x_class_init, + .abstract = true, + }, + { + .name = TYPE_PCA9546, + .parent = TYPE_PCA954X, + .class_init = pca9546_class_init, + }, + { + .name = TYPE_PCA9548, + .parent = TYPE_PCA954X, + .class_init = pca9548_class_init, + }, + { + .name = TYPE_PCA954X_CHANNEL, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = pca954x_channel_class_init, + .instance_size = sizeof(Pca954xChannel), + .instance_init = pca954x_channel_init, + } +}; + +DEFINE_TYPES(pca954x_info) diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index cdcd694a7f..dd3aef02b2 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -14,4 +14,5 @@ i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c')) i2c_ss.add(when: 'CONFIG_VERSATILE_I2C', if_true: files('versatile_i2c.c')) i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) +i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index 82fe6f965f..82f19e6a2d 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -26,3 +26,8 @@ npcm7xx_smbus_recv_byte(const char *id, uint8_t value) "%s recv byte: 0x%02x" npcm7xx_smbus_stop(const char *id) "%s stopping" npcm7xx_smbus_nack(const char *id) "%s nacking" npcm7xx_smbus_recv_fifo(const char *id, uint8_t received, uint8_t expected) "%s recv fifo: received %u, expected %u" + +# i2c-mux-pca954x.c + +pca954x_write_bytes(uint8_t value) "PCA954X write data: 0x%02x" +pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index bfecb0038c..80bee00da6 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1815,6 +1815,7 @@ build_hpet(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { Acpi20Hpet *hpet; + int hpet_start = table_data->len; hpet = acpi_data_push(table_data, sizeof(*hpet)); /* Note timer_block_id value must be kept in sync with value advertised by @@ -1823,13 +1824,15 @@ build_hpet(GArray *table_data, BIOSLinker *linker, const char *oem_id, hpet->timer_block_id = cpu_to_le32(0x8086a201); hpet->addr.address = cpu_to_le64(HPET_BASE); build_header(linker, table_data, - (void *)hpet, "HPET", sizeof(*hpet), 1, oem_id, oem_table_id); + (void *)(table_data->data + hpet_start), + "HPET", sizeof(*hpet), 1, oem_id, oem_table_id); } static void build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, const char *oem_id, const char *oem_table_id) { + int tcpa_start = table_data->len; Acpi20Tcpa *tcpa = acpi_data_push(table_data, sizeof *tcpa); unsigned log_addr_size = sizeof(tcpa->log_area_start_address); unsigned log_addr_offset = @@ -1848,7 +1851,8 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, ACPI_BUILD_TPMLOG_FILE, 0); build_header(linker, table_data, - (void *)tcpa, "TCPA", sizeof(*tcpa), 2, oem_id, oem_table_id); + (void *)(table_data->data + tcpa_start), + "TCPA", sizeof(*tcpa), 2, oem_id, oem_table_id); } #define HOLE_640K_START (640 * KiB) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 74a93a5d93..2801dff97c 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -99,7 +99,7 @@ static uint64_t amdvi_readq(AMDVIState *s, hwaddr addr) } /* internal write */ -static void amdvi_writeq_raw(AMDVIState *s, uint64_t val, hwaddr addr) +static void amdvi_writeq_raw(AMDVIState *s, hwaddr addr, uint64_t val) { stq_le_p(&s->mmior[addr], val); } @@ -382,7 +382,7 @@ static void amdvi_completion_wait(AMDVIState *s, uint64_t *cmd) } /* set completion interrupt */ if (extract64(cmd[0], 1, 1)) { - amdvi_test_mask(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_COMP_INT); + amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_COMP_INT); /* generate interrupt */ amdvi_generate_msi_interrupt(s); } @@ -553,7 +553,7 @@ static void amdvi_cmdbuf_run(AMDVIState *s) trace_amdvi_command_exec(s->cmdbuf_head, s->cmdbuf_tail, s->cmdbuf); amdvi_cmdbuf_exec(s); s->cmdbuf_head += AMDVI_COMMAND_SIZE; - amdvi_writeq_raw(s, s->cmdbuf_head, AMDVI_MMIO_COMMAND_HEAD); + amdvi_writeq_raw(s, AMDVI_MMIO_COMMAND_HEAD, s->cmdbuf_head); /* wrap head pointer */ if (s->cmdbuf_head >= s->cmdbuf_len * AMDVI_COMMAND_SIZE) { @@ -860,8 +860,8 @@ static inline uint8_t get_pte_translation_mode(uint64_t pte) static inline uint64_t pte_override_page_mask(uint64_t pte) { - uint8_t page_mask = 12; - uint64_t addr = (pte & AMDVI_DEV_PT_ROOT_MASK) ^ AMDVI_DEV_PT_ROOT_MASK; + uint8_t page_mask = 13; + uint64_t addr = (pte & AMDVI_DEV_PT_ROOT_MASK) >> 12; /* find the first zero bit */ while (addr & 1) { page_mask++; diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index e48a54fa36..4e68d5dea4 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -22,6 +22,7 @@ #include "hw/nvram/fw_cfg.h" #include "e820_memory_layout.h" #include "kvm/kvm_i386.h" +#include "qapi/error.h" #include CONFIG_DEVICES struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; @@ -78,7 +79,8 @@ void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg) } smbios_get_tables(ms, mem_array, array_count, &smbios_tables, &smbios_tables_len, - &smbios_anchor, &smbios_anchor_len); + &smbios_anchor, &smbios_anchor_len, + &error_fatal); g_free(mem_array); if (smbios_anchor) { diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index e82b7dcdd2..bd47c3d672 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -169,7 +169,8 @@ static void xen_remap_bucket(MapCacheEntry *entry, if (entry->vaddr_base != NULL) { if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { - ram_block_notify_remove(entry->vaddr_base, entry->size); + ram_block_notify_remove(entry->vaddr_base, entry->size, + entry->size); } /* @@ -224,7 +225,7 @@ static void xen_remap_bucket(MapCacheEntry *entry, } if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { - ram_block_notify_add(vaddr_base, size); + ram_block_notify_add(vaddr_base, size, size); } entry->vaddr_base = vaddr_base; @@ -465,7 +466,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) } pentry->next = entry->next; - ram_block_notify_remove(entry->vaddr_base, entry->size); + ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); if (munmap(entry->vaddr_base, entry->size) != 0) { perror("unmap fails"); exit(-1); diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index 85daf73f1a..137efba57b 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -193,13 +193,16 @@ static void virtio_input_host_handle_status(VirtIOInput *vinput, { VirtIOInputHost *vih = VIRTIO_INPUT_HOST(vinput); struct input_event evdev; + struct timeval tval; int rc; - if (gettimeofday(&evdev.time, NULL)) { + if (gettimeofday(&tval, NULL)) { perror("virtio_input_host_handle_status: gettimeofday"); return; } + evdev.input_event_sec = tval.tv_sec; + evdev.input_event_usec = tval.tv_usec; evdev.type = le16_to_cpu(event->type); evdev.code = le16_to_cpu(event->code); evdev.value = le32_to_cpu(event->value); diff --git a/hw/mem/meson.build b/hw/mem/meson.build index ef79e04678..3c8fdef9f9 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -1,8 +1,9 @@ mem_ss = ss.source_set() mem_ss.add(files('memory-device.c')) -mem_ss.add(when: 'CONFIG_FUZZ', if_true: files('sparse-mem.c')) mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c')) mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c')) mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c')) softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) + +softmmu_ss.add(when: 'CONFIG_FUZZ', if_true: files('sparse-mem.c')) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 12b655eda8..a3a2560301 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -34,6 +34,16 @@ static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); +static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) +{ + if (!dimm->hostmem) { + error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set"); + return NULL; + } + + return host_memory_backend_get_memory(dimm->hostmem); +} + void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, const uint64_t *legacy_align, Error **errp) { @@ -66,9 +76,8 @@ void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) { - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, - &error_abort); + MemoryRegion *vmstate_mr = pc_dimm_get_memory_region(dimm, + &error_abort); memory_device_plug(MEMORY_DEVICE(dimm), machine); vmstate_register_ram(vmstate_mr, DEVICE(dimm)); @@ -76,9 +85,8 @@ void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine) { - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, - &error_abort); + MemoryRegion *vmstate_mr = pc_dimm_get_memory_region(dimm, + &error_abort); memory_device_unplug(MEMORY_DEVICE(dimm), machine); vmstate_unregister_ram(vmstate_mr, DEVICE(dimm)); @@ -205,16 +213,6 @@ static void pc_dimm_unrealize(DeviceState *dev) host_memory_backend_set_mapped(dimm->hostmem, false); } -static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) -{ - if (!dimm->hostmem) { - error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set"); - return NULL; - } - - return host_memory_backend_get_memory(dimm->hostmem); -} - static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md) { return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP, @@ -266,7 +264,6 @@ static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, static void pc_dimm_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); dc->realize = pc_dimm_realize; @@ -274,8 +271,6 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, pc_dimm_properties); dc->desc = "DIMM memory module"; - ddc->get_vmstate_memory_region = pc_dimm_get_memory_region; - mdc->get_addr = pc_dimm_md_get_addr; mdc->set_addr = pc_dimm_md_set_addr; /* for a dimm plugged_size == region_size */ diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 66b9ff4511..6b7e8dd04e 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -89,7 +89,7 @@ VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \ VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) -static VirtIOFeature feature_sizes[] = { +static const VirtIOFeature feature_sizes[] = { {.flags = 1ULL << VIRTIO_NET_F_MAC, .end = endof(struct virtio_net_config, mac)}, {.flags = 1ULL << VIRTIO_NET_F_STATUS, diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 34b3538beb..1698d3a192 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -3,7 +3,7 @@ pci_ss.add(when: 'CONFIG_PAM', if_true: files('pam.c')) pci_ss.add(when: 'CONFIG_PCI_BONITO', if_true: files('bonito.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_DESIGNWARE', if_true: files('designware.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', if_true: files('gpex.c')) -pci_ss.add(when: 'CONFIG_ACPI', if_true: files('gpex-acpi.c')) +pci_ss.add(when: ['CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', 'CONFIG_ACPI'], if_true: files('gpex-acpi.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_Q35', if_true: files('q35.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_XILINX', if_true: files('xilinx-pcie.c')) pci_ss.add(when: 'CONFIG_PCI_I440FX', if_true: files('i440fx.c')) diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c index 9ce31526e8..e67a5de72c 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -218,7 +218,7 @@ uint64_t mpqemu_msg_send_and_await_reply(MPQemuMsg *msg, PCIProxyDev *pdev, bool mpqemu_msg_valid(MPQemuMsg *msg) { - if (msg->cmd >= MPQEMU_CMD_MAX && msg->cmd < 0) { + if (msg->cmd >= MPQEMU_CMD_MAX || msg->cmd < 0) { return false; } diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 3d2d3854e7..4fbafddb22 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -871,22 +871,6 @@ static void rtc_notify_suspend(Notifier *notifier, void *data) rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); } -static void rtc_reset(void *opaque) -{ - RTCState *s = opaque; - - s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); - s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); - check_update_timer(s); - - qemu_irq_lower(s->irq); - - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - s->irq_coalesced = 0; - s->irq_reinject_on_ack_count = 0; - } -} - static const MemoryRegionOps cmos_ops = { .read = cmos_ioport_read, .write = cmos_ioport_write, @@ -961,7 +945,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) memory_region_add_coalescing(&s->coalesced_io, 0, 1); qdev_set_legacy_instance_id(dev, RTC_ISA_BASE, 3); - qemu_register_reset(rtc_reset, s); object_property_add_tm(OBJECT(s), "date", rtc_get_date); @@ -997,15 +980,32 @@ static Property mc146818rtc_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void rtc_resetdev(DeviceState *d) +static void rtc_reset_enter(Object *obj, ResetType type) { - RTCState *s = MC146818_RTC(d); + RTCState *s = MC146818_RTC(obj); /* Reason: VM do suspend self will set 0xfe * Reset any values other than 0xfe(Guest suspend case) */ if (s->cmos_data[0x0f] != 0xfe) { s->cmos_data[0x0f] = 0x00; } + + s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); + s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); + check_update_timer(s); + + + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + s->irq_coalesced = 0; + s->irq_reinject_on_ack_count = 0; + } +} + +static void rtc_reset_hold(Object *obj) +{ + RTCState *s = MC146818_RTC(obj); + + qemu_irq_lower(s->irq); } static void rtc_build_aml(ISADevice *isadev, Aml *scope) @@ -1032,11 +1032,13 @@ static void rtc_build_aml(ISADevice *isadev, Aml *scope) static void rtc_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); dc->realize = rtc_realizefn; - dc->reset = rtc_resetdev; dc->vmsd = &vmstate_rtc; + rc->phases.enter = rtc_reset_enter; + rc->phases.hold = rtc_reset_hold; isa->build_aml = rtc_build_aml; device_class_set_props(dc, mc146818rtc_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 4ad8793406..28e003250a 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -94,8 +94,7 @@ static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, return progress; } -static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, - VirtIOHandleAIOOutput fn) +static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); int rc; @@ -109,7 +108,6 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, return rc; } - virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); return 0; } @@ -154,40 +152,55 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) goto fail_guest_notifiers; } - aio_context_acquire(s->ctx); - rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, - virtio_scsi_data_plane_handle_ctrl); - if (rc) { - goto fail_vrings; + memory_region_transaction_begin(); + + rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0); + if (rc != 0) { + goto fail_host_notifiers; } vq_init_count++; - rc = virtio_scsi_vring_init(s, vs->event_vq, 1, - virtio_scsi_data_plane_handle_event); - if (rc) { - goto fail_vrings; + rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1); + if (rc != 0) { + goto fail_host_notifiers; } vq_init_count++; + for (i = 0; i < vs->conf.num_queues; i++) { - rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, - virtio_scsi_data_plane_handle_cmd); + rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2); if (rc) { - goto fail_vrings; + goto fail_host_notifiers; } vq_init_count++; } + memory_region_transaction_commit(); + + aio_context_acquire(s->ctx); + virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, + virtio_scsi_data_plane_handle_ctrl); + virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, + virtio_scsi_data_plane_handle_event); + + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, + virtio_scsi_data_plane_handle_cmd); + } + s->dataplane_starting = false; s->dataplane_started = true; aio_context_release(s->ctx); return 0; -fail_vrings: - aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); - aio_context_release(s->ctx); +fail_host_notifiers: for (i = 0; i < vq_init_count; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + memory_region_transaction_commit(); + + for (i = 0; i < vq_init_count; i++) { virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); } k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); @@ -225,8 +238,15 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) blk_drain_all(); /* ensure there are no in-flight requests */ + memory_region_transaction_begin(); + for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + memory_region_transaction_commit(); + + for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); } diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index f22c4f5b73..7397e56737 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -27,6 +27,7 @@ #include "hw/firmware/smbios.h" #include "hw/loader.h" #include "hw/boards.h" +#include "hw/pci/pci_bus.h" #include "smbios_build.h" /* legacy structures and constants for <= 2.0 machines */ @@ -118,6 +119,28 @@ static struct { uint16_t speed; } type17; +static QEnumLookup type41_kind_lookup = { + .array = (const char *const[]) { + "other", + "unknown", + "video", + "scsi", + "ethernet", + "tokenring", + "sound", + "pata", + "sata", + "sas", + }, + .size = 10 +}; +struct type41_instance { + const char *designation, *pcidev; + uint8_t instance, kind; + QTAILQ_ENTRY(type41_instance) next; +}; +static QTAILQ_HEAD(, type41_instance) type41 = QTAILQ_HEAD_INITIALIZER(type41); + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -358,6 +381,32 @@ static const QemuOptDesc qemu_smbios_type17_opts[] = { { /* end of list */ } }; +static const QemuOptDesc qemu_smbios_type41_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "designation", + .type = QEMU_OPT_STRING, + .help = "reference designation string", + },{ + .name = "kind", + .type = QEMU_OPT_STRING, + .help = "device type", + .def_value_str = "other", + },{ + .name = "instance", + .type = QEMU_OPT_NUMBER, + .help = "device type instance", + },{ + .name = "pcidev", + .type = QEMU_OPT_STRING, + .help = "PCI device", + }, + { /* end of list */ } +}; + static void smbios_register_config(void) { qemu_add_opts(&qemu_smbios_opts); @@ -773,6 +822,53 @@ static void smbios_build_type_32_table(void) SMBIOS_BUILD_TABLE_POST; } +static void smbios_build_type_41_table(Error **errp) +{ + unsigned instance = 0; + struct type41_instance *t41; + + QTAILQ_FOREACH(t41, &type41, next) { + SMBIOS_BUILD_TABLE_PRE(41, 0x2900 + instance, true); + + SMBIOS_TABLE_SET_STR(41, reference_designation_str, t41->designation); + t->device_type = t41->kind; + t->device_type_instance = t41->instance; + t->segment_group_number = cpu_to_le16(0); + t->bus_number = 0; + t->device_number = 0; + + if (t41->pcidev) { + PCIDevice *pdev = NULL; + int rc = pci_qdev_find_device(t41->pcidev, &pdev); + if (rc != 0) { + error_setg(errp, + "No PCI device %s for SMBIOS type 41 entry %s", + t41->pcidev, t41->designation); + return; + } + /* + * We only handle the case were the device is attached to + * the PCI root bus. The general case is more complex as + * bridges are enumerated later and the table would need + * to be updated at this moment. + */ + if (!pci_bus_is_root(pci_get_bus(pdev))) { + error_setg(errp, + "Cannot create type 41 entry for PCI device %s: " + "not attached to the root bus", + t41->pcidev); + return; + } + t->segment_group_number = cpu_to_le16(0); + t->bus_number = pci_dev_bus_num(pdev); + t->device_number = pdev->devfn; + } + + SMBIOS_BUILD_TABLE_POST; + instance++; + } +} + static void smbios_build_type_127_table(void) { SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */ @@ -883,7 +979,8 @@ void smbios_get_tables(MachineState *ms, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, uint8_t **tables, size_t *tables_len, - uint8_t **anchor, size_t *anchor_len) + uint8_t **anchor, size_t *anchor_len, + Error **errp) { unsigned i, dimm_cnt; @@ -928,6 +1025,7 @@ void smbios_get_tables(MachineState *ms, smbios_build_type_32_table(); smbios_build_type_38_table(); + smbios_build_type_41_table(errp); smbios_build_type_127_table(); smbios_validate_table(ms); @@ -1224,6 +1322,30 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) save_opt(&type17.part, opts, "part"); type17.speed = qemu_opt_get_number(opts, "speed", 0); return; + case 41: { + struct type41_instance *t; + Error *local_err = NULL; + + if (!qemu_opts_validate(opts, qemu_smbios_type41_opts, errp)) { + return; + } + t = g_new0(struct type41_instance, 1); + save_opt(&t->designation, opts, "designation"); + t->kind = qapi_enum_parse(&type41_kind_lookup, + qemu_opt_get(opts, "kind"), + 0, &local_err) + 1; + t->kind |= 0x80; /* enabled */ + if (local_err != NULL) { + error_propagate(errp, local_err); + g_free(t); + return; + } + t->instance = qemu_opt_get_number(opts, "instance", 1); + save_opt(&t->pcidev, opts, "pcidev"); + + QTAILQ_INSERT_TAIL(&type41, t, next); + return; + } default: error_setg(errp, "Don't know how to build fields for SMBIOS type %ld", diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index 5379006086..4ba662190d 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -309,9 +309,9 @@ static const MemoryRegionOps timer_ops = { } }; -static void etraxfs_timer_reset(void *opaque) +static void etraxfs_timer_reset_enter(Object *obj, ResetType type) { - ETRAXTimerState *t = opaque; + ETRAXTimerState *t = ETRAX_TIMER(obj); ptimer_transaction_begin(t->ptimer_t0); ptimer_stop(t->ptimer_t0); @@ -325,6 +325,12 @@ static void etraxfs_timer_reset(void *opaque) t->rw_wd_ctrl = 0; t->r_intr = 0; t->rw_intr_mask = 0; +} + +static void etraxfs_timer_reset_hold(Object *obj) +{ + ETRAXTimerState *t = ETRAX_TIMER(obj); + qemu_irq_lower(t->irq); } @@ -343,14 +349,16 @@ static void etraxfs_timer_realize(DeviceState *dev, Error **errp) memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, "etraxfs-timer", 0x5c); sysbus_init_mmio(sbd, &t->mmio); - qemu_register_reset(etraxfs_timer_reset, t); } static void etraxfs_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->realize = etraxfs_timer_realize; + rc->phases.enter = etraxfs_timer_reset_enter; + rc->phases.hold = etraxfs_timer_reset_hold; } static const TypeInfo etraxfs_timer_info = { diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 01d2101d09..8f2fb9f10b 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -371,8 +371,8 @@ static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) return 0; } -int vhost_vdpa_get_device_id(struct vhost_dev *dev, - uint32_t *device_id) +static int vhost_vdpa_get_device_id(struct vhost_dev *dev, + uint32_t *device_id) { int ret; ret = vhost_vdpa_call(dev, VHOST_VDPA_GET_DEVICE_ID, device_id); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index d120bf8f43..4b5d9e5e50 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -663,9 +663,6 @@ virtio_balloon_free_page_hint_notify(NotifierWithReturn *n, void *data) } switch (pnd->reason) { - case PRECOPY_NOTIFY_SETUP: - precopy_enable_free_page_optimization(); - break; case PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC: virtio_balloon_free_page_stop(dev); break; @@ -685,6 +682,7 @@ virtio_balloon_free_page_hint_notify(NotifierWithReturn *n, void *data) */ virtio_balloon_free_page_done(dev); break; + case PRECOPY_NOTIFY_SETUP: case PRECOPY_NOTIFY_COMPLETE: break; default: diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 655824ff81..75aa7d6f1b 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -902,9 +902,6 @@ static int virtio_mem_precopy_notify(NotifierWithReturn *n, void *data) PrecopyNotifyData *pnd = data; switch (pnd->reason) { - case PRECOPY_NOTIFY_SETUP: - precopy_enable_free_page_optimization(); - break; case PRECOPY_NOTIFY_AFTER_BITMAP_SYNC: virtio_mem_precopy_exclude_unplugged(vmem); break; diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 342c918ea7..5952471b38 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -36,7 +36,9 @@ static bool virtio_mmio_ioeventfd_enabled(DeviceState *d) { - return kvm_eventfds_enabled(); + VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); + + return (proxy->flags & VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD) != 0; } static int virtio_mmio_ioeventfd_assign(DeviceState *d, @@ -720,6 +722,8 @@ static Property virtio_mmio_properties[] = { DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy, format_transport_address, true), DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), + DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags, + VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -731,6 +735,11 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) qbus_create_inplace(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, d, NULL); sysbus_init_irq(sbd, &proxy->irq); + + if (!kvm_eventfds_enabled()) { + proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; + } + if (proxy->legacy) { memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_legacy_mem_ops, proxy, diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9e13cb9e3a..e02544b2df 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2981,7 +2981,7 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } -size_t virtio_feature_get_config_size(VirtIOFeature *feature_sizes, +size_t virtio_feature_get_config_size(const VirtIOFeature *feature_sizes, uint64_t host_features) { size_t config_size = 0; diff --git a/include/block/block_int.h b/include/block/block_int.h index c823f5b1b3..b2c8b09d0f 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -357,7 +357,7 @@ struct BlockDriver { * of in-flight requests, so don't waste the time if possible. * * One example usage is to avoid waiting for an nbd target node reconnect - * timeout during job-cancel. + * timeout during job-cancel with force=true. */ void (*bdrv_cancel_in_flight)(BlockDriverState *bs); @@ -954,12 +954,8 @@ struct BlockDriverState { */ int64_t total_sectors; - /* Callback before write request is processed */ - NotifierWithReturnList before_write_notifiers; - /* threshold limit for writes, in bytes. "High water mark". */ uint64_t write_threshold_offset; - NotifierWithReturn write_threshold_notifier; /* Writing to the list requires the BQL _and_ the dirty_bitmap_mutex. * Reading from the list can be done with either the BQL or the @@ -1085,15 +1081,6 @@ bool bdrv_backing_overridden(BlockDriverState *bs); /** - * bdrv_add_before_write_notifier: - * - * Register a callback that is invoked before write requests are processed but - * after any throttling or waiting for overlapping requests. - */ -void bdrv_add_before_write_notifier(BlockDriverState *bs, - NotifierWithReturn *notifier); - -/** * bdrv_add_aio_context_notifier: * * If a long-running job intends to be always run in the same AioContext as a diff --git a/include/block/write-threshold.h b/include/block/write-threshold.h index c646f267a4..f50f923e7e 100644 --- a/include/block/write-threshold.h +++ b/include/block/write-threshold.h @@ -13,7 +13,7 @@ #ifndef BLOCK_WRITE_THRESHOLD_H #define BLOCK_WRITE_THRESHOLD_H -#include "block/block_int.h" +#include "qemu/typedefs.h" /* * bdrv_write_threshold_set: @@ -36,27 +36,12 @@ void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes); uint64_t bdrv_write_threshold_get(const BlockDriverState *bs); /* - * bdrv_write_threshold_is_set + * bdrv_write_threshold_check_write * - * Tell if a write threshold is set for a given BDS. + * Check whether the specified request exceeds the write threshold. + * If so, send a corresponding event and disable write threshold checking. */ -bool bdrv_write_threshold_is_set(const BlockDriverState *bs); - -/* - * bdrv_write_threshold_exceeded - * - * Return the extent of a write request that exceeded the threshold, - * or zero if the request is below the threshold. - * Return zero also if the threshold was not set. - * - * NOTE: here we assume the following holds for each request this code - * deals with: - * - * assert((req->offset + req->bytes) <= UINT64_MAX) - * - * Please not there is *not* an actual C assert(). - */ -uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, - const BdrvTrackedRequest *req); +void bdrv_write_threshold_check_write(BlockDriverState *bs, int64_t offset, + int64_t bytes); #endif diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 5a0a2d93e0..ccabed4003 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -57,6 +57,7 @@ const char *qemu_ram_get_idstr(RAMBlock *rb); void *qemu_ram_get_host_addr(RAMBlock *rb); ram_addr_t qemu_ram_get_offset(RAMBlock *rb); ram_addr_t qemu_ram_get_used_length(RAMBlock *rb); +ram_addr_t qemu_ram_get_max_length(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); bool qemu_ram_is_uf_zeroable(RAMBlock *rb); void qemu_ram_set_uf_zeroable(RAMBlock *rb); diff --git a/include/exec/memory.h b/include/exec/memory.h index 5728a681b2..c8b9088924 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -131,7 +131,7 @@ typedef struct IOMMUTLBEvent { #define RAM_SHARED (1 << 1) /* Only a portion of RAM (used_length) is actually used, and migrated. - * This used_length size can change across reboots. + * Resizing RAM while migrating can result in the migration being canceled. */ #define RAM_RESIZEABLE (1 << 2) @@ -955,7 +955,9 @@ void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, * RAM. Accesses into the region will * modify memory directly. Only an initial * portion of this RAM is actually used. - * The used size can change across reboots. + * Changing the size while migrating + * can result in the migration being + * canceled. * * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count @@ -1586,8 +1588,8 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr); /* memory_region_ram_resize: Resize a RAM region. * - * Only legal before guest might have detected the memory size: e.g. on - * incoming migration, or right after reset. + * Resizing RAM while migrating can result in the migration being canceled. + * Care has to be taken if the guest might have already detected the memory. * * @mr: a memory region created with @memory_region_init_resizeable_ram. * @newsize: the new size the region diff --git a/include/exec/poison.h b/include/exec/poison.h index 8fc7530b6e..7ad4ad18e8 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -4,6 +4,8 @@ #ifndef HW_POISON_H #define HW_POISON_H +#include "config-poison.h" + #pragma GCC poison TARGET_I386 #pragma GCC poison TARGET_X86_64 #pragma GCC poison TARGET_AARCH64 @@ -83,8 +85,12 @@ #pragma GCC poison CONFIG_SPARC_DIS #pragma GCC poison CONFIG_XTENSA_DIS +#pragma GCC poison CONFIG_HAX +#pragma GCC poison CONFIG_HVF #pragma GCC poison CONFIG_LINUX_USER #pragma GCC poison CONFIG_KVM #pragma GCC poison CONFIG_SOFTMMU +#pragma GCC poison CONFIG_WHPX +#pragma GCC poison CONFIG_XEN #endif diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h index 07d50864d8..664701b759 100644 --- a/include/exec/ramblock.h +++ b/include/exec/ramblock.h @@ -59,6 +59,16 @@ struct RAMBlock { */ unsigned long *clear_bmap; uint8_t clear_bmap_shift; + + /* + * RAM block length that corresponds to the used_length on the migration + * source (after RAM block sizes were synchronized). Especially, after + * starting to run the guest, used_length and postcopy_length can differ. + * Used to register/unregister uffd handlers and as the size of the received + * bitmap. Receiving any page beyond this length will bail out, as it + * could not have been valid on the source. + */ + ram_addr_t postcopy_length; }; #endif #endif diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index 26704aa3b0..ece6497ee2 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -65,15 +65,20 @@ void qemu_mutex_lock_ramlist(void); void qemu_mutex_unlock_ramlist(void); struct RAMBlockNotifier { - void (*ram_block_added)(RAMBlockNotifier *n, void *host, size_t size); - void (*ram_block_removed)(RAMBlockNotifier *n, void *host, size_t size); + void (*ram_block_added)(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size); + void (*ram_block_removed)(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size); + void (*ram_block_resized)(RAMBlockNotifier *n, void *host, size_t old_size, + size_t new_size); QLIST_ENTRY(RAMBlockNotifier) next; }; void ram_block_notifier_add(RAMBlockNotifier *n); void ram_block_notifier_remove(RAMBlockNotifier *n); -void ram_block_notify_add(void *host, size_t size); -void ram_block_notify_remove(void *host, size_t size); +void ram_block_notify_add(void *host, size_t size, size_t max_size); +void ram_block_notify_remove(void *host, size_t size, size_t max_size); +void ram_block_notify_resize(void *host, size_t old_size, size_t new_size); void ram_block_dump(Monitor *mon); diff --git a/include/fpu/softfloat-macros.h b/include/fpu/softfloat-macros.h index a35ec2893a..ec4e27a595 100644 --- a/include/fpu/softfloat-macros.h +++ b/include/fpu/softfloat-macros.h @@ -83,6 +83,43 @@ this code that are retained. #define FPU_SOFTFLOAT_MACROS_H #include "fpu/softfloat-types.h" +#include "qemu/host-utils.h" + +/** + * shl_double: double-word merging left shift + * @l: left or most-significant word + * @r: right or least-significant word + * @c: shift count + * + * Shift @l left by @c bits, shifting in bits from @r. + */ +static inline uint64_t shl_double(uint64_t l, uint64_t r, int c) +{ +#if defined(__x86_64__) + asm("shld %b2, %1, %0" : "+r"(l) : "r"(r), "ci"(c)); + return l; +#else + return c ? (l << c) | (r >> (64 - c)) : l; +#endif +} + +/** + * shr_double: double-word merging right shift + * @l: left or most-significant word + * @r: right or least-significant word + * @c: shift count + * + * Shift @r right by @c bits, shifting in bits from @l. + */ +static inline uint64_t shr_double(uint64_t l, uint64_t r, int c) +{ +#if defined(__x86_64__) + asm("shrd %b2, %1, %0" : "+r"(r) : "r"(l), "ci"(c)); + return r; +#else + return c ? (r >> c) | (l << (64 - c)) : r; +#endif +} /*---------------------------------------------------------------------------- | Shifts `a' right by the number of bits given in `count'. If any nonzero @@ -403,16 +440,12 @@ static inline void | are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -static inline void - add128( - uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr ) +static inline void add128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, + uint64_t *z0Ptr, uint64_t *z1Ptr) { - uint64_t z1; - - z1 = a1 + b1; - *z1Ptr = z1; - *z0Ptr = a0 + b0 + ( z1 < a1 ); - + bool c = 0; + *z1Ptr = uadd64_carry(a1, b1, &c); + *z0Ptr = uadd64_carry(a0, b0, &c); } /*---------------------------------------------------------------------------- @@ -423,34 +456,14 @@ static inline void | `z1Ptr', and `z2Ptr'. *----------------------------------------------------------------------------*/ -static inline void - add192( - uint64_t a0, - uint64_t a1, - uint64_t a2, - uint64_t b0, - uint64_t b1, - uint64_t b2, - uint64_t *z0Ptr, - uint64_t *z1Ptr, - uint64_t *z2Ptr - ) +static inline void add192(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t b0, uint64_t b1, uint64_t b2, + uint64_t *z0Ptr, uint64_t *z1Ptr, uint64_t *z2Ptr) { - uint64_t z0, z1, z2; - int8_t carry0, carry1; - - z2 = a2 + b2; - carry1 = ( z2 < a2 ); - z1 = a1 + b1; - carry0 = ( z1 < a1 ); - z0 = a0 + b0; - z1 += carry1; - z0 += ( z1 < carry1 ); - z0 += carry0; - *z2Ptr = z2; - *z1Ptr = z1; - *z0Ptr = z0; - + bool c = 0; + *z2Ptr = uadd64_carry(a2, b2, &c); + *z1Ptr = uadd64_carry(a1, b1, &c); + *z0Ptr = uadd64_carry(a0, b0, &c); } /*---------------------------------------------------------------------------- @@ -461,14 +474,12 @@ static inline void | `z1Ptr'. *----------------------------------------------------------------------------*/ -static inline void - sub128( - uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr ) +static inline void sub128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, + uint64_t *z0Ptr, uint64_t *z1Ptr) { - - *z1Ptr = a1 - b1; - *z0Ptr = a0 - b0 - ( a1 < b1 ); - + bool c = 0; + *z1Ptr = usub64_borrow(a1, b1, &c); + *z0Ptr = usub64_borrow(a0, b0, &c); } /*---------------------------------------------------------------------------- @@ -479,34 +490,14 @@ static inline void | pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'. *----------------------------------------------------------------------------*/ -static inline void - sub192( - uint64_t a0, - uint64_t a1, - uint64_t a2, - uint64_t b0, - uint64_t b1, - uint64_t b2, - uint64_t *z0Ptr, - uint64_t *z1Ptr, - uint64_t *z2Ptr - ) +static inline void sub192(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t b0, uint64_t b1, uint64_t b2, + uint64_t *z0Ptr, uint64_t *z1Ptr, uint64_t *z2Ptr) { - uint64_t z0, z1, z2; - int8_t borrow0, borrow1; - - z2 = a2 - b2; - borrow1 = ( a2 < b2 ); - z1 = a1 - b1; - borrow0 = ( a1 < b1 ); - z0 = a0 - b0; - z0 -= ( z1 < borrow1 ); - z1 -= borrow1; - z0 -= borrow0; - *z2Ptr = z2; - *z1Ptr = z1; - *z0Ptr = z0; - + bool c = 0; + *z2Ptr = usub64_borrow(a2, b2, &c); + *z1Ptr = usub64_borrow(a1, b1, &c); + *z0Ptr = usub64_borrow(a0, b0, &c); } /*---------------------------------------------------------------------------- @@ -515,27 +506,10 @@ static inline void | `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -static inline void mul64To128( uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr ) +static inline void +mul64To128(uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr) { - uint32_t aHigh, aLow, bHigh, bLow; - uint64_t z0, zMiddleA, zMiddleB, z1; - - aLow = a; - aHigh = a>>32; - bLow = b; - bHigh = b>>32; - z1 = ( (uint64_t) aLow ) * bLow; - zMiddleA = ( (uint64_t) aLow ) * bHigh; - zMiddleB = ( (uint64_t) aHigh ) * bLow; - z0 = ( (uint64_t) aHigh ) * bHigh; - zMiddleA += zMiddleB; - z0 += ( ( (uint64_t) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); - zMiddleA <<= 32; - z1 += zMiddleA; - z0 += ( z1 < zMiddleA ); - *z1Ptr = z1; - *z0Ptr = z0; - + mulu64(z1Ptr, z0Ptr, a, b); } /*---------------------------------------------------------------------------- @@ -546,24 +520,14 @@ static inline void mul64To128( uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *----------------------------------------------------------------------------*/ static inline void - mul128By64To192( - uint64_t a0, - uint64_t a1, - uint64_t b, - uint64_t *z0Ptr, - uint64_t *z1Ptr, - uint64_t *z2Ptr - ) +mul128By64To192(uint64_t a0, uint64_t a1, uint64_t b, + uint64_t *z0Ptr, uint64_t *z1Ptr, uint64_t *z2Ptr) { - uint64_t z0, z1, z2, more1; - - mul64To128( a1, b, &z1, &z2 ); - mul64To128( a0, b, &z0, &more1 ); - add128( z0, more1, 0, z1, &z0, &z1 ); - *z2Ptr = z2; - *z1Ptr = z1; - *z0Ptr = z0; + uint64_t z0, z1, m1; + mul64To128(a1, b, &m1, z2Ptr); + mul64To128(a0, b, &z0, &z1); + add128(z0, z1, 0, m1, z0Ptr, z1Ptr); } /*---------------------------------------------------------------------------- @@ -573,34 +537,21 @@ static inline void | the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'. *----------------------------------------------------------------------------*/ -static inline void - mul128To256( - uint64_t a0, - uint64_t a1, - uint64_t b0, - uint64_t b1, - uint64_t *z0Ptr, - uint64_t *z1Ptr, - uint64_t *z2Ptr, - uint64_t *z3Ptr - ) +static inline void mul128To256(uint64_t a0, uint64_t a1, + uint64_t b0, uint64_t b1, + uint64_t *z0Ptr, uint64_t *z1Ptr, + uint64_t *z2Ptr, uint64_t *z3Ptr) { - uint64_t z0, z1, z2, z3; - uint64_t more1, more2; - - mul64To128( a1, b1, &z2, &z3 ); - mul64To128( a1, b0, &z1, &more2 ); - add128( z1, more2, 0, z2, &z1, &z2 ); - mul64To128( a0, b0, &z0, &more1 ); - add128( z0, more1, 0, z1, &z0, &z1 ); - mul64To128( a0, b1, &more1, &more2 ); - add128( more1, more2, 0, z2, &more1, &z2 ); - add128( z0, z1, 0, more1, &z0, &z1 ); - *z3Ptr = z3; - *z2Ptr = z2; - *z1Ptr = z1; - *z0Ptr = z0; + uint64_t z0, z1, z2; + uint64_t m0, m1, m2, n1, n2; + + mul64To128(a1, b0, &m1, &m2); + mul64To128(a0, b1, &n1, &n2); + mul64To128(a1, b1, &z2, z3Ptr); + mul64To128(a0, b0, &z0, &z1); + add192( 0, m1, m2, 0, n1, n2, &m0, &m1, &m2); + add192(m0, m1, m2, z0, z1, z2, z0Ptr, z1Ptr, z2Ptr); } /*---------------------------------------------------------------------------- diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 78ad5ca738..53f2c2ea3c 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -100,7 +100,10 @@ typedef enum { | Routine to raise any or all of the software IEC/IEEE floating-point | exception flags. *----------------------------------------------------------------------------*/ -void float_raise(uint8_t flags, float_status *status); +static inline void float_raise(uint8_t flags, float_status *status) +{ + status->float_exception_flags |= flags; +} /*---------------------------------------------------------------------------- | If `a' is denormal and we are in flush-to-zero mode then set the @@ -1194,6 +1197,8 @@ float128 float128_round_to_int(float128, float_status *status); float128 float128_add(float128, float128, float_status *status); float128 float128_sub(float128, float128, float_status *status); float128 float128_mul(float128, float128, float_status *status); +float128 float128_muladd(float128, float128, float128, int, + float_status *status); float128 float128_div(float128, float128, float_status *status); float128 float128_rem(float128, float128, float_status *status); float128 float128_sqrt(float128, float_status *status); diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 02a0ced0a0..5a0dd0c8cf 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -258,6 +258,17 @@ struct smbios_type_32 { uint8_t boot_status; } QEMU_PACKED; +/* SMBIOS type 41 - Onboard Devices Extended Information */ +struct smbios_type_41 { + struct smbios_structure_header header; + uint8_t reference_designation_str; + uint8_t device_type; + uint8_t device_type_instance; + uint16_t segment_group_number; + uint8_t bus_number; + uint8_t device_number; +} QEMU_PACKED; + /* SMBIOS type 127 -- End-of-table */ struct smbios_type_127 { struct smbios_structure_header header; @@ -273,5 +284,6 @@ void smbios_get_tables(MachineState *ms, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, uint8_t **tables, size_t *tables_len, - uint8_t **anchor, size_t *anchor_len); + uint8_t **anchor, size_t *anchor_len, + Error **errp); #endif /* QEMU_SMBIOS_H */ diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 277dd9f2d6..ff4129ea70 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -16,6 +16,7 @@ enum i2c_event { I2C_NACK /* Masker NACKed a receive byte. */ }; +typedef struct I2CNodeList I2CNodeList; #define TYPE_I2C_SLAVE "i2c-slave" OBJECT_DECLARE_TYPE(I2CSlave, I2CSlaveClass, @@ -39,6 +40,16 @@ struct I2CSlaveClass { * return code is not used and should be zero. */ int (*event)(I2CSlave *s, enum i2c_event event); + + /* + * Check if this device matches the address provided. Returns bool of + * true if it matches (or broadcast), and updates the device list, false + * otherwise. + * + * If broadcast is true, match should add the device and return true. + */ + bool (*match_and_add)(I2CSlave *candidate, uint8_t address, bool broadcast, + I2CNodeList *current_devs); }; struct I2CSlave { @@ -58,9 +69,11 @@ struct I2CNode { QLIST_ENTRY(I2CNode) next; }; +typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; + struct I2CBus { BusState qbus; - QLIST_HEAD(, I2CNode) current_devs; + I2CNodeList current_devs; uint8_t saved_address; bool broadcast; }; @@ -74,6 +87,8 @@ void i2c_nack(I2CBus *bus); int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send); int i2c_send(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); /** * Create an I2C slave device on the heap. diff --git a/include/hw/i2c/i2c_mux_pca954x.h b/include/hw/i2c/i2c_mux_pca954x.h new file mode 100644 index 0000000000..8aaf9bbc39 --- /dev/null +++ b/include/hw/i2c/i2c_mux_pca954x.h @@ -0,0 +1,19 @@ +#ifndef QEMU_I2C_MUX_PCA954X +#define QEMU_I2C_MUX_PCA954X + +#include "hw/i2c/i2c.h" + +#define TYPE_PCA9546 "pca9546" +#define TYPE_PCA9548 "pca9548" + +/** + * Retrieves the i2c bus associated with the specified channel on this i2c + * mux. + * @mux: an i2c mux device. + * @channel: the i2c channel requested + * + * Returns: a pointer to the associated i2c bus. + */ +I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel); + +#endif diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 3d3db82641..1473e6db62 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -56,9 +56,6 @@ struct PCDIMMDevice { * PCDIMMDeviceClass: * @realize: called after common dimm is realized so that the dimm based * devices get the chance to do specified operations. - * @get_vmstate_memory_region: returns #MemoryRegion which indicates the - * memory of @dimm should be kept during live migration. Will not fail - * after the device was realized. */ struct PCDIMMDeviceClass { /* private */ @@ -66,8 +63,6 @@ struct PCDIMMDeviceClass { /* public */ void (*realize)(PCDIMMDevice *dimm, Error **errp); - MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm, - Error **errp); }; void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 9b81a409da..28ca65018e 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -22,6 +22,4 @@ typedef struct vhost_vdpa { } VhostVDPA; extern AddressSpace address_space_memory; -extern int vhost_vdpa_get_device_id(struct vhost_dev *dev, - uint32_t *device_id); #endif diff --git a/include/hw/virtio/virtio-mmio.h b/include/hw/virtio/virtio-mmio.h index d4c4c386ab..090f7730e7 100644 --- a/include/hw/virtio/virtio-mmio.h +++ b/include/hw/virtio/virtio-mmio.h @@ -49,12 +49,17 @@ typedef struct VirtIOMMIOQueue { uint32_t used[2]; } VirtIOMMIOQueue; +#define VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT 1 +#define VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD \ + (1 << VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT) + struct VirtIOMMIOProxy { /* Generic */ SysBusDevice parent_obj; MemoryRegion iomem; qemu_irq irq; bool legacy; + uint32_t flags; /* Guest accessible state needing migration and reset */ uint32_t host_features_sel; uint32_t guest_features_sel; diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index b7ece7a6a8..8bab9cfb75 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -43,7 +43,7 @@ typedef struct VirtIOFeature { size_t end; } VirtIOFeature; -size_t virtio_feature_get_config_size(VirtIOFeature *features, +size_t virtio_feature_get_config_size(const VirtIOFeature *features, uint64_t host_features); typedef struct VirtQueue VirtQueue; diff --git a/include/migration/misc.h b/include/migration/misc.h index 738675ef52..465906710d 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -37,7 +37,6 @@ void precopy_infrastructure_init(void); void precopy_add_notifier(NotifierWithReturn *n); void precopy_remove_notifier(NotifierWithReturn *n); int precopy_notify(PrecopyNotifyReason reason, Error **errp); -void precopy_enable_free_page_optimization(void); void ram_mig_init(void); void qemu_guest_free_page_hint(void *addr, size_t len); diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index cdca2991d8..711b221704 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -26,6 +26,7 @@ #ifndef HOST_UTILS_H #define HOST_UTILS_H +#include "qemu/compiler.h" #include "qemu/bswap.h" #ifdef CONFIG_INT128 @@ -272,6 +273,9 @@ static inline int ctpop64(uint64_t val) */ static inline uint8_t revbit8(uint8_t x) { +#if __has_builtin(__builtin_bitreverse8) + return __builtin_bitreverse8(x); +#else /* Assign the correct nibble position. */ x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4); @@ -281,6 +285,7 @@ static inline uint8_t revbit8(uint8_t x) | ((x & 0x22) << 1) | ((x & 0x11) << 3); return x; +#endif } /** @@ -289,6 +294,9 @@ static inline uint8_t revbit8(uint8_t x) */ static inline uint16_t revbit16(uint16_t x) { +#if __has_builtin(__builtin_bitreverse16) + return __builtin_bitreverse16(x); +#else /* Assign the correct byte position. */ x = bswap16(x); /* Assign the correct nibble position. */ @@ -300,6 +308,7 @@ static inline uint16_t revbit16(uint16_t x) | ((x & 0x2222) << 1) | ((x & 0x1111) << 3); return x; +#endif } /** @@ -308,6 +317,9 @@ static inline uint16_t revbit16(uint16_t x) */ static inline uint32_t revbit32(uint32_t x) { +#if __has_builtin(__builtin_bitreverse32) + return __builtin_bitreverse32(x); +#else /* Assign the correct byte position. */ x = bswap32(x); /* Assign the correct nibble position. */ @@ -319,6 +331,7 @@ static inline uint32_t revbit32(uint32_t x) | ((x & 0x22222222u) << 1) | ((x & 0x11111111u) << 3); return x; +#endif } /** @@ -327,6 +340,9 @@ static inline uint32_t revbit32(uint32_t x) */ static inline uint64_t revbit64(uint64_t x) { +#if __has_builtin(__builtin_bitreverse64) + return __builtin_bitreverse64(x); +#else /* Assign the correct byte position. */ x = bswap64(x); /* Assign the correct nibble position. */ @@ -338,6 +354,281 @@ static inline uint64_t revbit64(uint64_t x) | ((x & 0x2222222222222222ull) << 1) | ((x & 0x1111111111111111ull) << 3); return x; +#endif +} + +/** + * sadd32_overflow - addition with overflow indication + * @x, @y: addends + * @ret: Output for sum + * + * Computes *@ret = @x + @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * sadd64_overflow - addition with overflow indication + * @x, @y: addends + * @ret: Output for sum + * + * Computes *@ret = @x + @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * uadd32_overflow - addition with overflow indication + * @x, @y: addends + * @ret: Output for sum + * + * Computes *@ret = @x + @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * uadd64_overflow - addition with overflow indication + * @x, @y: addends + * @ret: Output for sum + * + * Computes *@ret = @x + @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * ssub32_overflow - subtraction with overflow indication + * @x: Minuend + * @y: Subtrahend + * @ret: Output for difference + * + * Computes *@ret = @x - @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * ssub64_overflow - subtraction with overflow indication + * @x: Minuend + * @y: Subtrahend + * @ret: Output for sum + * + * Computes *@ret = @x - @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * usub32_overflow - subtraction with overflow indication + * @x: Minuend + * @y: Subtrahend + * @ret: Output for sum + * + * Computes *@ret = @x - @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * usub64_overflow - subtraction with overflow indication + * @x: Minuend + * @y: Subtrahend + * @ret: Output for sum + * + * Computes *@ret = @x - @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * smul32_overflow - multiplication with overflow indication + * @x, @y: Input multipliers + * @ret: Output for product + * + * Computes *@ret = @x * @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * smul64_overflow - multiplication with overflow indication + * @x, @y: Input multipliers + * @ret: Output for product + * + * Computes *@ret = @x * @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * umul32_overflow - multiplication with overflow indication + * @x, @y: Input multipliers + * @ret: Output for product + * + * Computes *@ret = @x * @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * umul64_overflow - multiplication with overflow indication + * @x, @y: Input multipliers + * @ret: Output for product + * + * Computes *@ret = @x * @y, and returns true if and only if that + * value has been truncated. + */ +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 +} + +/** + * uadd64_carry - addition with carry-in and carry-out + * @x, @y: addends + * @pcarry: in-out carry value + * + * Computes @x + @y + *@pcarry, placing the carry-out back + * into *@pcarry and returning the 64-bit sum. + */ +static inline uint64_t uadd64_carry(uint64_t x, uint64_t y, bool *pcarry) +{ +#if __has_builtin(__builtin_addcll) + unsigned long long c = *pcarry; + x = __builtin_addcll(x, y, c, &c); + *pcarry = c & 1; + return x; +#else + bool c = *pcarry; + /* This is clang's internal expansion of __builtin_addc. */ + c = uadd64_overflow(x, c, &x); + c |= uadd64_overflow(x, y, &x); + *pcarry = c; + return x; +#endif +} + +/** + * usub64_borrow - subtraction with borrow-in and borrow-out + * @x, @y: addends + * @pborrow: in-out borrow value + * + * Computes @x - @y - *@pborrow, placing the borrow-out back + * into *@pborrow and returning the 64-bit sum. + */ +static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) +{ +#if __has_builtin(__builtin_subcll) + unsigned long long b = *pborrow; + x = __builtin_subcll(x, y, b, &b); + *pborrow = b & 1; + return x; +#else + bool b = *pborrow; + b = usub64_overflow(x, b, &x); + b |= usub64_overflow(x, y, &x); + *pborrow = b; + return x; +#endif } /* Host type specific sizes of these routines. */ diff --git a/include/qemu/job.h b/include/qemu/job.h index efc6fa7544..41162ed494 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -254,7 +254,7 @@ struct JobDriver { /** * If the callback is not NULL, it will be invoked in job_cancel_async */ - void (*cancel)(Job *job); + void (*cancel)(Job *job, bool force); /** Called when the job is freed */ diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h index 12fb54f990..247f0661d1 100644 --- a/include/sysemu/hax.h +++ b/include/sysemu/hax.h @@ -24,6 +24,8 @@ int hax_sync_vcpus(void); +#ifdef NEED_CPU_H + #ifdef CONFIG_HAX int hax_enabled(void); @@ -34,4 +36,6 @@ int hax_enabled(void); #endif /* CONFIG_HAX */ +#endif /* NEED_CPU_H */ + #endif /* QEMU_HAX_H */ diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index c98636bc81..bb70082e45 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -16,6 +16,8 @@ #include "qemu/accel.h" #include "qom/object.h" +#ifdef NEED_CPU_H + #ifdef CONFIG_HVF uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); @@ -26,6 +28,8 @@ extern bool hvf_allowed; #define hvf_get_supported_cpuid(func, idx, reg) 0 #endif /* !CONFIG_HVF */ +#endif /* NEED_CPU_H */ + #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") typedef struct HVFState HVFState; diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 8ca1c1c4ac..2889fa2278 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -13,6 +13,8 @@ #ifndef QEMU_WHPX_H #define QEMU_WHPX_H +#ifdef NEED_CPU_H + #ifdef CONFIG_WHPX int whpx_enabled(void); @@ -25,4 +27,6 @@ bool whpx_apic_in_platform(void); #endif /* CONFIG_WHPX */ +#endif /* NEED_CPU_H */ + #endif /* QEMU_WHPX_H */ diff --git a/job.c b/job.c index 4aff13d95a..8775c1803b 100644 --- a/job.c +++ b/job.c @@ -716,7 +716,7 @@ static int job_finalize_single(Job *job) static void job_cancel_async(Job *job, bool force) { if (job->driver->cancel) { - job->driver->cancel(job); + job->driver->cancel(job, force); } if (job->user_paused) { /* Do not call job_enter here, the caller will handle it. */ diff --git a/migration/meson.build b/migration/meson.build index 3ecedce94d..f8714dcb15 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -31,4 +31,5 @@ softmmu_ss.add(when: ['CONFIG_RDMA', rdma], if_true: files('rdma.c')) softmmu_ss.add(when: 'CONFIG_LIVE_BLOCK_MIGRATION', if_true: files('block.c')) softmmu_ss.add(when: zstd, if_true: files('multifd-zstd.c')) -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files('dirtyrate.c', 'ram.c')) +specific_ss.add(when: 'CONFIG_SOFTMMU', + if_true: files('dirtyrate.c', 'ram.c', 'target.c')) diff --git a/migration/migration.c b/migration/migration.c index 8ca034136b..1885860d7b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -60,10 +60,6 @@ #include "qemu/yank.h" #include "sysemu/cpus.h" -#ifdef CONFIG_VFIO -#include "hw/vfio/vfio-common.h" -#endif - #define MAX_THROTTLE (128 << 20) /* Migration transfer speed throttling */ /* Amount of time to allocate to each "chunk" of bandwidth-throttled @@ -223,13 +219,18 @@ void migration_object_init(void) dirty_bitmap_mig_init(); } +void migration_cancel(void) +{ + migrate_fd_cancel(current_migration); +} + void migration_shutdown(void) { /* * Cancel the current migration - that will (eventually) * stop the migration using this structure */ - migrate_fd_cancel(current_migration); + migration_cancel(); object_unref(OBJECT(current_migration)); /* @@ -1059,41 +1060,27 @@ static void populate_disk_info(MigrationInfo *info) } } -static void populate_vfio_info(MigrationInfo *info) -{ -#ifdef CONFIG_VFIO - if (vfio_mig_active()) { - info->has_vfio = true; - info->vfio = g_malloc0(sizeof(*info->vfio)); - info->vfio->transferred = vfio_mig_bytes_transferred(); - } -#endif -} - static void fill_source_migration_info(MigrationInfo *info) { MigrationState *s = migrate_get_current(); + GSList *cur_blocker = migration_blockers; - info->blocked = migration_is_blocked(NULL); - info->has_blocked_reasons = info->blocked; info->blocked_reasons = NULL; - if (info->blocked) { - GSList *cur_blocker = migration_blockers; - /* - * There are two types of reasons a migration might be blocked; - * a) devices marked in VMState as non-migratable, and - * b) Explicit migration blockers - * We need to add both of them here. - */ - qemu_savevm_non_migratable_list(&info->blocked_reasons); + /* + * There are two types of reasons a migration might be blocked; + * a) devices marked in VMState as non-migratable, and + * b) Explicit migration blockers + * We need to add both of them here. + */ + qemu_savevm_non_migratable_list(&info->blocked_reasons); - while (cur_blocker) { - QAPI_LIST_PREPEND(info->blocked_reasons, - g_strdup(error_get_pretty(cur_blocker->data))); - cur_blocker = g_slist_next(cur_blocker); - } + while (cur_blocker) { + QAPI_LIST_PREPEND(info->blocked_reasons, + g_strdup(error_get_pretty(cur_blocker->data))); + cur_blocker = g_slist_next(cur_blocker); } + info->has_blocked_reasons = info->blocked_reasons != NULL; switch (s->state) { case MIGRATION_STATUS_NONE: @@ -2310,7 +2297,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, void qmp_migrate_cancel(Error **errp) { - migrate_fd_cancel(migrate_get_current()); + migration_cancel(); } void qmp_migrate_continue(MigrationStatus state, Error **errp) diff --git a/migration/migration.h b/migration/migration.h index db6708326b..b88bd8fe07 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -375,5 +375,8 @@ int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); void migration_make_urgent_request(void); void migration_consume_urgent_request(void); bool migration_rate_limit(void); +void migration_cancel(void); + +void populate_vfio_info(MigrationInfo *info); #endif diff --git a/migration/multifd.c b/migration/multifd.c index a6677c45c8..0a4803cfcc 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -361,7 +361,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) if (offset > (block->used_length - qemu_target_page_size())) { error_setg(errp, "multifd: offset too long %" PRIu64 " (max " RAM_ADDR_FMT ")", - offset, block->max_length); + offset, block->used_length); return -1; } p->pages->iov[i].iov_base = block->host + offset; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index ab482adef1..2e9697bdd2 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -17,6 +17,7 @@ */ #include "qemu/osdep.h" +#include "qemu/rcu.h" #include "exec/target_page.h" #include "migration.h" #include "qemu-file.h" @@ -30,6 +31,7 @@ #include "qemu/error-report.h" #include "trace.h" #include "hw/boards.h" +#include "exec/ramblock.h" /* Arbitrary limit on size of each discard command, * keeps them around ~200 bytes @@ -453,6 +455,13 @@ static int init_range(RAMBlock *rb, void *opaque) trace_postcopy_init_range(block_name, host_addr, offset, length); /* + * Save the used_length before running the guest. In case we have to + * resize RAM blocks when syncing RAM block sizes from the source during + * precopy, we'll update it manually via the ram block notifier. + */ + rb->postcopy_length = length; + + /* * We need the whole of RAM to be truly empty for postcopy, so things * like ROMs and any data tables built during init must be zero'd * - we're going to get the copy from the source anyway. @@ -474,7 +483,7 @@ static int cleanup_range(RAMBlock *rb, void *opaque) const char *block_name = qemu_ram_get_idstr(rb); void *host_addr = qemu_ram_get_host_addr(rb); ram_addr_t offset = qemu_ram_get_offset(rb); - ram_addr_t length = qemu_ram_get_used_length(rb); + ram_addr_t length = rb->postcopy_length; MigrationIncomingState *mis = opaque; struct uffdio_range range_struct; trace_postcopy_cleanup_range(block_name, host_addr, offset, length); @@ -580,7 +589,7 @@ static int nhp_range(RAMBlock *rb, void *opaque) const char *block_name = qemu_ram_get_idstr(rb); void *host_addr = qemu_ram_get_host_addr(rb); ram_addr_t offset = qemu_ram_get_offset(rb); - ram_addr_t length = qemu_ram_get_used_length(rb); + ram_addr_t length = rb->postcopy_length; trace_postcopy_nhp_range(block_name, host_addr, offset, length); /* @@ -624,7 +633,7 @@ static int ram_block_enable_notify(RAMBlock *rb, void *opaque) struct uffdio_register reg_struct; reg_struct.range.start = (uintptr_t)qemu_ram_get_host_addr(rb); - reg_struct.range.len = qemu_ram_get_used_length(rb); + reg_struct.range.len = rb->postcopy_length; reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING; /* Now tell our userfault_fd that it's responsible for this area */ diff --git a/migration/ram.c b/migration/ram.c index ace8ad431c..60ea913c54 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -240,7 +240,7 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, return -1; } - nbits = block->used_length >> TARGET_PAGE_BITS; + nbits = block->postcopy_length >> TARGET_PAGE_BITS; /* * Make sure the tmp bitmap buffer is big enough, e.g., on 32bit @@ -311,10 +311,6 @@ struct RAMState { ram_addr_t last_page; /* last ram version we have seen */ uint32_t last_version; - /* We are in the first round */ - bool ram_bulk_stage; - /* The free page optimization is enabled */ - bool fpo_enabled; /* How many times we have dirty too many pages */ int dirty_rate_high_cnt; /* these variables are used for bitmap sync */ @@ -330,6 +326,8 @@ struct RAMState { uint64_t xbzrle_pages_prev; /* Amount of xbzrle encoded bytes since the beginning of the period */ uint64_t xbzrle_bytes_prev; + /* Start using XBZRLE (e.g., after the first round). */ + bool xbzrle_enabled; /* compression statistics since the beginning of the period */ /* amount of count that no free thread to compress data */ @@ -383,15 +381,6 @@ int precopy_notify(PrecopyNotifyReason reason, Error **errp) return notifier_with_return_list_notify(&precopy_notifier_list, &pnd); } -void precopy_enable_free_page_optimization(void) -{ - if (!ram_state) { - return; - } - - ram_state->fpo_enabled = true; -} - uint64_t ram_bytes_remaining(void) { return ram_state ? (ram_state->migration_dirty_pages * TARGET_PAGE_SIZE) : @@ -664,7 +653,7 @@ static void mig_throttle_guest_down(uint64_t bytes_dirty_period, */ static void xbzrle_cache_zero_page(RAMState *rs, ram_addr_t current_addr) { - if (rs->ram_bulk_stage || !migrate_use_xbzrle()) { + if (!rs->xbzrle_enabled) { return; } @@ -792,23 +781,12 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, { unsigned long size = rb->used_length >> TARGET_PAGE_BITS; unsigned long *bitmap = rb->bmap; - unsigned long next; if (ramblock_is_ignored(rb)) { return size; } - /* - * When the free page optimization is enabled, we need to check the bitmap - * to send the non-free pages rather than all the pages in the bulk stage. - */ - if (!rs->fpo_enabled && rs->ram_bulk_stage && start > 0) { - next = start + 1; - } else { - next = find_next_bit(bitmap, size, start); - } - - return next; + return find_next_bit(bitmap, size, start); } static inline bool migration_bitmap_clear_dirty(RAMState *rs, @@ -1185,8 +1163,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) trace_ram_save_page(block->idstr, (uint64_t)offset, p); XBZRLE_cache_lock(); - if (!rs->ram_bulk_stage && !migration_in_postcopy() && - migrate_use_xbzrle()) { + if (rs->xbzrle_enabled && !migration_in_postcopy()) { pages = save_xbzrle_page(rs, &p, current_addr, block, offset, last_stage); if (!last_stage) { @@ -1365,8 +1342,8 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) *again = false; return false; } - if ((((ram_addr_t)pss->page) << TARGET_PAGE_BITS) - >= pss->block->used_length) { + if (!offset_in_ramblock(pss->block, + ((ram_addr_t)pss->page) << TARGET_PAGE_BITS)) { /* Didn't find anything in this RAM Block */ pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); @@ -1386,7 +1363,10 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) pss->block = QLIST_FIRST_RCU(&ram_list.blocks); /* Flag that we've looped */ pss->complete_round = true; - rs->ram_bulk_stage = false; + /* After the first round, enable XBZRLE. */ + if (migrate_use_xbzrle()) { + rs->xbzrle_enabled = true; + } } /* Didn't find anything this time, but try again on the new block */ *again = true; @@ -1801,14 +1781,6 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) if (block) { /* - * As soon as we start servicing pages out of order, then we have - * to kill the bulk stage, since the bulk stage assumes - * in (migration_bitmap_find_and_reset_dirty) that every page is - * dirty, that's no longer true. - */ - rs->ram_bulk_stage = false; - - /* * We want the background search to continue from the queued page * since the guest is likely to want other pages near to the page * it just requested. @@ -1891,7 +1863,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) rs->last_req_rb = ramblock; } trace_ram_save_queue_pages(ramblock->idstr, start, len); - if (start + len > ramblock->used_length) { + if (!offset_in_ramblock(ramblock, start + len - 1)) { error_report("%s request overrun start=" RAM_ADDR_FMT " len=" RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, __func__, start, len, ramblock->used_length); @@ -1920,15 +1892,15 @@ static bool save_page_use_compression(RAMState *rs) } /* - * If xbzrle is on, stop using the data compression after first - * round of migration even if compression is enabled. In theory, - * xbzrle can do better than compression. + * If xbzrle is enabled (e.g., after first round of migration), stop + * using the data compression. In theory, xbzrle can do better than + * compression. */ - if (rs->ram_bulk_stage || !migrate_use_xbzrle()) { - return true; + if (rs->xbzrle_enabled) { + return false; } - return false; + return true; } /* @@ -2041,6 +2013,8 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, int tmppages, pages = 0; size_t pagesize_bits = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; + unsigned long hostpage_boundary = + QEMU_ALIGN_UP(pss->page + 1, pagesize_bits); unsigned long start_page = pss->page; int res; @@ -2051,25 +2025,27 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, do { /* Check the pages is dirty and if it is send it */ - if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { - pss->page++; - continue; - } + if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { + tmppages = ram_save_target_page(rs, pss, last_stage); + if (tmppages < 0) { + return tmppages; + } - tmppages = ram_save_target_page(rs, pss, last_stage); - if (tmppages < 0) { - return tmppages; + pages += tmppages; + /* + * Allow rate limiting to happen in the middle of huge pages if + * something is sent in the current iteration. + */ + if (pagesize_bits > 1 && tmppages > 0) { + migration_rate_limit(); + } } - - pages += tmppages; - pss->page++; - /* Allow rate limiting to happen in the middle of huge pages */ - migration_rate_limit(); - } while ((pss->page & (pagesize_bits - 1)) && + pss->page = migration_bitmap_find_dirty(rs, pss->block, pss->page); + } while ((pss->page < hostpage_boundary) && offset_in_ramblock(pss->block, ((ram_addr_t)pss->page) << TARGET_PAGE_BITS)); - /* The offset we leave with is the last one we looked at */ - pss->page--; + /* The offset we leave with is the min boundary of host page and block */ + pss->page = MIN(pss->page, hostpage_boundary) - 1; res = ram_save_release_protection(rs, pss, start_page); return (res < 0 ? res : pages); @@ -2235,8 +2211,7 @@ static void ram_state_reset(RAMState *rs) rs->last_sent_block = NULL; rs->last_page = 0; rs->last_version = ram_list.version; - rs->ram_bulk_stage = true; - rs->fpo_enabled = false; + rs->xbzrle_enabled = false; } #define MAX_WAIT 50 /* ms, half buffered_file limit */ @@ -2720,15 +2695,7 @@ static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) /* This may not be aligned with current bitmaps. Recalculate. */ rs->migration_dirty_pages = pages; - rs->last_seen_block = NULL; - rs->last_sent_block = NULL; - rs->last_page = 0; - rs->last_version = ram_list.version; - /* - * Disable the bulk stage, otherwise we'll resend the whole RAM no - * matter what we have sent. - */ - rs->ram_bulk_stage = false; + ram_state_reset(rs); /* Update RAMState cache of output QEMUFile */ rs->f = out; @@ -3118,6 +3085,20 @@ static inline void *host_from_ram_block_offset(RAMBlock *block, return block->host + offset; } +static void *host_page_from_ram_block_offset(RAMBlock *block, + ram_addr_t offset) +{ + /* Note: Explicitly no check against offset_in_ramblock(). */ + return (void *)QEMU_ALIGN_DOWN((uintptr_t)(block->host + offset), + block->page_size); +} + +static ram_addr_t host_page_offset_from_ram_block_offset(RAMBlock *block, + ram_addr_t offset) +{ + return ((uintptr_t)block->host + offset) & (block->page_size - 1); +} + static inline void *colo_cache_from_block_offset(RAMBlock *block, ram_addr_t offset, bool record_bitmap) { @@ -3345,16 +3326,9 @@ static void decompress_data_with_multi_threads(QEMUFile *f, } } - /* - * we must set ram_bulk_stage to false, otherwise in - * migation_bitmap_find_dirty the bitmap will be unused and - * all the pages in ram cache wil be flushed to the ram of - * secondary VM. - */ static void colo_init_ram_state(void) { ram_state_init(&ram_state); - ram_state->ram_bulk_stage = false; } /* @@ -3521,13 +3495,12 @@ static int ram_load_postcopy(QEMUFile *f) MigrationIncomingState *mis = migration_incoming_get_current(); /* Temporary page that is later 'placed' */ void *postcopy_host_page = mis->postcopy_tmp_page; - void *this_host = NULL; + void *host_page = NULL; bool all_zero = true; int target_pages = 0; while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr; - void *host = NULL; void *page_buffer = NULL; void *place_source = NULL; RAMBlock *block = NULL; @@ -3552,9 +3525,18 @@ static int ram_load_postcopy(QEMUFile *f) if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE)) { block = ram_block_from_stream(f, flags); + if (!block) { + ret = -EINVAL; + break; + } - host = host_from_ram_block_offset(block, addr); - if (!host) { + /* + * Relying on used_length is racy and can result in false positives. + * We might place pages beyond used_length in case RAM was shrunk + * while in postcopy, which is fine - trying to place via + * UFFDIO_COPY/UFFDIO_ZEROPAGE will never segfault. + */ + if (!block->host || addr >= block->postcopy_length) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); ret = -EINVAL; break; @@ -3572,19 +3554,17 @@ static int ram_load_postcopy(QEMUFile *f) * of a host page in one chunk. */ page_buffer = postcopy_host_page + - ((uintptr_t)host & (block->page_size - 1)); + host_page_offset_from_ram_block_offset(block, addr); + /* If all TP are zero then we can optimise the place */ if (target_pages == 1) { - this_host = (void *)QEMU_ALIGN_DOWN((uintptr_t)host, - block->page_size); - } else { + host_page = host_page_from_ram_block_offset(block, addr); + } else if (host_page != host_page_from_ram_block_offset(block, + addr)) { /* not the 1st TP within the HP */ - if (QEMU_ALIGN_DOWN((uintptr_t)host, block->page_size) != - (uintptr_t)this_host) { - error_report("Non-same host page %p/%p", - host, this_host); - ret = -EINVAL; - break; - } + error_report("Non-same host page %p/%p", host_page, + host_page_from_ram_block_offset(block, addr)); + ret = -EINVAL; + break; } /* @@ -3663,16 +3643,11 @@ static int ram_load_postcopy(QEMUFile *f) } if (!ret && place_needed) { - /* This gets called at the last target page in the host page */ - void *place_dest = (void *)QEMU_ALIGN_DOWN((uintptr_t)host, - block->page_size); - if (all_zero) { - ret = postcopy_place_page_zero(mis, place_dest, - block); + ret = postcopy_place_page_zero(mis, host_page, block); } else { - ret = postcopy_place_page(mis, place_dest, - place_source, block); + ret = postcopy_place_page(mis, host_page, place_source, + block); } place_needed = false; target_pages = 0; @@ -3721,8 +3696,8 @@ void colo_flush_ram_cache(void) while (block) { offset = migration_bitmap_find_dirty(ram_state, block, offset); - if (((ram_addr_t)offset) << TARGET_PAGE_BITS - >= block->used_length) { + if (!offset_in_ramblock(block, + ((ram_addr_t)offset) << TARGET_PAGE_BITS)) { offset = 0; block = QLIST_NEXT_RCU(block, next); } else { @@ -4136,8 +4111,69 @@ static SaveVMHandlers savevm_ram_handlers = { .resume_prepare = ram_resume_prepare, }; +static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, + size_t old_size, size_t new_size) +{ + PostcopyState ps = postcopy_state_get(); + ram_addr_t offset; + RAMBlock *rb = qemu_ram_block_from_host(host, false, &offset); + Error *err = NULL; + + if (ramblock_is_ignored(rb)) { + return; + } + + if (!migration_is_idle()) { + /* + * Precopy code on the source cannot deal with the size of RAM blocks + * changing at random points in time - especially after sending the + * RAM block sizes in the migration stream, they must no longer change. + * Abort and indicate a proper reason. + */ + error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr); + migrate_set_error(migrate_get_current(), err); + error_free(err); + migration_cancel(); + } + + switch (ps) { + case POSTCOPY_INCOMING_ADVISE: + /* + * Update what ram_postcopy_incoming_init()->init_range() does at the + * time postcopy was advised. Syncing RAM blocks with the source will + * result in RAM resizes. + */ + if (old_size < new_size) { + if (ram_discard_range(rb->idstr, old_size, new_size - old_size)) { + error_report("RAM block '%s' discard of resized RAM failed", + rb->idstr); + } + } + rb->postcopy_length = new_size; + break; + case POSTCOPY_INCOMING_NONE: + case POSTCOPY_INCOMING_RUNNING: + case POSTCOPY_INCOMING_END: + /* + * Once our guest is running, postcopy does no longer care about + * resizes. When growing, the new memory was not available on the + * source, no handler needed. + */ + break; + default: + error_report("RAM block '%s' resized during postcopy state: %d", + rb->idstr, ps); + exit(-1); + } +} + +static RAMBlockNotifier ram_mig_ram_notifier = { + .ram_block_resized = ram_mig_ram_block_resized, +}; + void ram_mig_init(void) { qemu_mutex_init(&XBZRLE.lock); register_savevm_live("ram", 0, 4, &savevm_ram_handlers, &ram_state); + ram_block_notifier_add(&ram_mig_ram_notifier); } diff --git a/migration/target.c b/migration/target.c new file mode 100644 index 0000000000..907ebf0a0a --- /dev/null +++ b/migration/target.c @@ -0,0 +1,25 @@ +/* + * QEMU live migration - functions that need to be compiled target-specific + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-types-migration.h" +#include "migration.h" + +#ifdef CONFIG_VFIO +#include "hw/vfio/vfio-common.h" +#endif + +void populate_vfio_info(MigrationInfo *info) +{ +#ifdef CONFIG_VFIO + if (vfio_mig_active()) { + info->has_vfio = true; + info->vfio = g_malloc0(sizeof(*info->vfio)); + info->vfio->transferred = vfio_mig_bytes_transferred(); + } +#endif +} diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 0ad5b77477..d9bef63373 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -224,7 +224,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) migration_global_dump(mon); - if (info->blocked) { + if (info->blocked_reasons) { strList *reasons = info->blocked_reasons; monitor_printf(mon, "Outgoing migration blocked:\n"); while (reasons) { diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h index dfcfea0ff0..3d0731c4c6 100644 --- a/pc-bios/s390-ccw/helper.h +++ b/pc-bios/s390-ccw/helper.h @@ -31,7 +31,7 @@ static inline void *u32toptr(uint32_t n) static inline void yield(void) { - asm volatile ("diag 0,0,0x44" + asm volatile ("diag %%r0,%%r0,0x44" : : : "memory", "cc"); } diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index 73e4367e09..78f5f46533 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -64,8 +64,8 @@ void jump_to_IPL_code(uint64_t address) * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 * can then use r15 as its stack pointer. */ - asm volatile("lghi 1,1\n\t" - "diag 1,1,0x308\n\t" + asm volatile("lghi %%r1,1\n\t" + "diag %%r1,%%r1,0x308\n\t" : : : "1", "memory"); panic("\n! IPL returns !\n"); } diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index de8260a5d6..d601952d3e 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -36,9 +36,9 @@ static inline void enable_clock_int(void) uint64_t tmp = 0; asm volatile( - "stctg 0,0,%0\n" + "stctg %%c0,%%c0,%0\n" "oi 6+%0, 0x8\n" - "lctlg 0,0,%0" + "lctlg %%c0,%%c0,%0" : : "Q" (tmp) : "memory" ); } @@ -48,9 +48,9 @@ static inline void disable_clock_int(void) uint64_t tmp = 0; asm volatile( - "stctg 0,0,%0\n" + "stctg %%c0,%%c0,%0\n" "ni 6+%0, 0xf7\n" - "lctlg 0,0,%0" + "lctlg %%c0,%%c0,%0" : : "Q" (tmp) : "memory" ); } diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index ab49840db8..5d2c6e3381 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -54,7 +54,7 @@ static long kvm_hypercall(unsigned long nr, unsigned long param1, register ulong r_param3 asm("4") = param3; register long retval asm("2"); - asm volatile ("diag 2,4,0x500" + asm volatile ("diag %%r2,%%r4,0x500" : "=d" (retval) : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3) : "memory", "cc"); diff --git a/qapi/migration.json b/qapi/migration.json index 0b17cce46b..7a5bdf9a0d 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -228,11 +228,6 @@ # Present and non-empty when migration is blocked. # (since 6.0) # -# @blocked: True if outgoing migration is blocked (since 6.0) -# -# Features: -# @deprecated: Member @blocked is deprecated. Use @blocked-reasons instead. -# # Since: 0.14 ## { 'struct': 'MigrationInfo', @@ -246,7 +241,6 @@ '*setup-time': 'int', '*cpu-throttle-percentage': 'int', '*error-desc': 'str', - 'blocked': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*blocked-reasons': ['str'], '*postcopy-blocktime' : 'uint32', '*postcopy-vcpu-blocktime': ['uint32'], diff --git a/qapi/qom.json b/qapi/qom.json index cd0e76d564..40d70c434a 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -251,8 +251,8 @@ # # @max_queue_size: the maximum number of packets to keep in the queue for # comparing with incoming packets from @secondary_in. If the -# queue is full and addtional packets are received, the -# addtional packets are dropped. (default: 1024) +# queue is full and additional packets are received, the +# additional packets are dropped. (default: 1024) # # @vnet_hdr_support: if true, vnet header support is enabled (default: false) # diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 97611969cb..998b67186d 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -2457,9 +2457,12 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; +/* + * Called with aio context of blk acquired. Or with qemu_get_aio_context() + * context acquired if blk is NULL. + */ int qemuio_command(BlockBackend *blk, const char *cmd) { - AioContext *ctx; char *input; const cmdinfo_t *ct; char **v; @@ -2471,10 +2474,7 @@ int qemuio_command(BlockBackend *blk, const char *cmd) if (c) { ct = find_command(v[0]); if (ct) { - ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); - aio_context_acquire(ctx); ret = command(blk, ct, c, v); - aio_context_release(ctx); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); ret = -EINVAL; diff --git a/qemu-io.c b/qemu-io.c index bf902302e9..57f07501df 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -411,6 +411,19 @@ static void prep_fetchline(void *opaque) *fetchable= 1; } +static int do_qemuio_command(const char *cmd) +{ + int ret; + AioContext *ctx = + qemuio_blk ? blk_get_aio_context(qemuio_blk) : qemu_get_aio_context(); + + aio_context_acquire(ctx); + ret = qemuio_command(qemuio_blk, cmd); + aio_context_release(ctx); + + return ret; +} + static int command_loop(void) { int i, fetchable = 0, prompted = 0; @@ -418,7 +431,7 @@ static int command_loop(void) char *input; for (i = 0; !quit_qemu_io && i < ncmdline; i++) { - ret = qemuio_command(qemuio_blk, cmdline[i]); + ret = do_qemuio_command(cmdline[i]); if (ret < 0) { last_error = ret; } @@ -446,7 +459,7 @@ static int command_loop(void) if (input == NULL) { break; } - ret = qemuio_command(qemuio_blk, input); + ret = do_qemuio_command(input); g_free(input); if (ret < 0) { diff --git a/qemu-options.hx b/qemu-options.hx index a81ca006db..e22fb94d99 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2370,7 +2370,9 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 11 fields\n" "-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str]\n" " [,asset=str][,part=str][,speed=%d]\n" - " specify SMBIOS type 17 fields\n", + " specify SMBIOS type 17 fields\n" + "-smbios type=41[,designation=str][,kind=str][,instance=%d][,pcidev=str]\n" + " specify SMBIOS type 41 fields\n", QEMU_ARCH_I386 | QEMU_ARCH_ARM) SRST ``-smbios file=binary`` @@ -2432,6 +2434,32 @@ SRST ``-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str][,asset=str][,part=str][,speed=%d]`` Specify SMBIOS type 17 fields + +``-smbios type=41[,designation=str][,kind=str][,instance=%d][,pcidev=str]`` + Specify SMBIOS type 41 fields + + This argument can be repeated multiple times. Its main use is to allow network interfaces be created + as ``enoX`` on Linux, with X being the instance number, instead of the name depending on the interface + position on the PCI bus. + + Here is an example of use: + + .. parsed-literal:: + + -netdev user,id=internet \\ + -device virtio-net-pci,mac=50:54:00:00:00:42,netdev=internet,id=internet-dev \\ + -smbios type=41,designation='Onboard LAN',instance=1,kind=ethernet,pcidev=internet-dev + + In the guest OS, the device should then appear as ``eno1``: + + ..parsed-literal:: + + $ ip -brief l + lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> + eno1 UP 50:54:00:00:00:42 <BROADCAST,MULTICAST,UP,LOWER_UP> + + Currently, the PCI device has to be attached to the root bus. + ERST DEFHEADING() diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8f7053ec9b..3d185cceac 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1532,6 +1532,7 @@ sub process { ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && (defined($1) || defined($2)))) && !(($realfile ne '') && + defined($acpi_testexpected) && ($realfile eq $acpi_testexpected))) { $reported_maintainer_file = 1; WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 5232696571..e1da81ed2f 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1694,6 +1694,11 @@ ram_addr_t qemu_ram_get_used_length(RAMBlock *rb) return rb->used_length; } +ram_addr_t qemu_ram_get_max_length(RAMBlock *rb) +{ + return rb->max_length; +} + bool qemu_ram_is_shared(RAMBlock *rb) { return rb->flags & RAM_SHARED; @@ -1793,8 +1798,9 @@ static int memory_try_enable_merging(void *addr, size_t len) return qemu_madvise(addr, len, QEMU_MADV_MERGEABLE); } -/* Only legal before guest might have detected the memory size: e.g. on - * incoming migration, or right after reset. +/* + * Resizing RAM while migrating can result in the migration being canceled. + * Care has to be taken if the guest might have already detected the memory. * * As memory core doesn't know how is memory accessed, it is up to * resize callback to update device state and/or add assertions to detect @@ -1802,6 +1808,7 @@ static int memory_try_enable_merging(void *addr, size_t len) */ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) { + const ram_addr_t oldsize = block->used_length; const ram_addr_t unaligned_size = newsize; assert(block); @@ -1838,6 +1845,11 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) return -EINVAL; } + /* Notify before modifying the ram block and touching the bitmaps. */ + if (block->host) { + ram_block_notify_resize(block->host, oldsize, newsize); + } + cpu_physical_memory_clear_dirty_range(block->offset, block->used_length); block->used_length = newsize; cpu_physical_memory_set_dirty_range(block->offset, block->used_length, @@ -2005,7 +2017,8 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK); } - ram_block_notify_add(new_block->host, new_block->max_length); + ram_block_notify_add(new_block->host, new_block->used_length, + new_block->max_length); } } @@ -2184,7 +2197,8 @@ void qemu_ram_free(RAMBlock *block) } if (block->host) { - ram_block_notify_remove(block->host, block->max_length); + ram_block_notify_remove(block->host, block->used_length, + block->max_length); } qemu_mutex_lock_ramlist(); @@ -3486,7 +3500,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) goto err; } - if ((start + length) <= rb->used_length) { + if ((start + length) <= rb->max_length) { bool need_madvise, need_fallocate; if (!QEMU_IS_ALIGNED(length, rb->page_size)) { error_report("ram_block_discard_range: Unaligned length: %zx", @@ -3553,7 +3567,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) } else { error_report("ram_block_discard_range: Overrun block '%s' (%" PRIu64 "/%zx/" RAM_ADDR_FMT")", - rb->idstr, start, length, rb->used_length); + rb->idstr, start, length, rb->max_length); } err: diff --git a/target/avr/helper.c b/target/avr/helper.c index 35e1019594..981c29da45 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -188,11 +188,7 @@ void helper_break(CPUAVRState *env) void helper_wdr(CPUAVRState *env) { - CPUState *cs = env_cpu(env); - - /* WD is not implemented yet, placeholder */ - cs->exception_index = EXCP_DEBUG; - cpu_loop_exit(cs); + qemu_log_mask(LOG_UNIMP, "WDG reset (not implemented)\n"); } /* diff --git a/target/i386/hax/hax-mem.c b/target/i386/hax/hax-mem.c index 35495f5e82..8d44edbffd 100644 --- a/target/i386/hax/hax-mem.c +++ b/target/i386/hax/hax-mem.c @@ -293,7 +293,8 @@ static MemoryListener hax_memory_listener = { .priority = 10, }; -static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) +static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size) { /* * We must register each RAM block with the HAXM kernel module, or @@ -304,7 +305,7 @@ static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) * host physical pages for the RAM block as part of this registration * process, hence the name hax_populate_ram(). */ - if (hax_populate_ram((uint64_t)(uintptr_t)host, size) < 0) { + if (hax_populate_ram((uint64_t)(uintptr_t)host, max_size) < 0) { fprintf(stderr, "HAX failed to populate RAM\n"); abort(); } diff --git a/target/i386/sev.c b/target/i386/sev.c index 9a43be11cb..41f7800b5f 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -180,7 +180,8 @@ sev_set_guest_state(SevGuestState *sev, SevState new_state) } static void -sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) +sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size) { int r; struct kvm_enc_region range; @@ -197,19 +198,20 @@ sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) } range.addr = (__u64)(unsigned long)host; - range.size = size; + range.size = max_size; - trace_kvm_memcrypt_register_region(host, size); + trace_kvm_memcrypt_register_region(host, max_size); r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); if (r) { error_report("%s: failed to register region (%p+%#zx) error '%s'", - __func__, host, size, strerror(errno)); + __func__, host, max_size, strerror(errno)); exit(1); } } static void -sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size) +sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size) { int r; struct kvm_enc_region range; @@ -226,13 +228,13 @@ sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size) } range.addr = (__u64)(unsigned long)host; - range.size = size; + range.size = max_size; - trace_kvm_memcrypt_unregister_region(host, size); + trace_kvm_memcrypt_unregister_region(host, max_size); r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); if (r) { error_report("%s: failed to unregister region (%p+%#zx)", - __func__, host, size); + __func__, host, max_size); } } diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index 1c2d6d35a7..ad1116e8c1 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -27,8 +27,14 @@ static inline void restore_flush_mode(CPUMIPSState *env) static inline void restore_snan_bit_mode(CPUMIPSState *env) { - set_snan_bit_is_one((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) == 0, - &env->active_fpu.fp_status); + bool nan2008 = env->active_fpu.fcr31 & (1 << FCR31_NAN2008); + + /* + * With nan2008, SNaNs are silenced in the usual way. + * Before that, SNaNs are not silenced; default nans are produced. + */ + set_snan_bit_is_one(!nan2008, &env->active_fpu.fp_status); + set_default_nan_mode(!nan2008, &env->active_fpu.fp_status); } static inline void restore_fp_status(CPUMIPSState *env) diff --git a/target/sh4/helper.c b/target/sh4/helper.c index bd8e034f17..2d622081e8 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -441,9 +441,12 @@ hwaddr superh_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) target_ulong physical; int prot; - get_physical_address(&cpu->env, &physical, &prot, addr, MMU_DATA_LOAD); + if (get_physical_address(&cpu->env, &physical, &prot, addr, MMU_DATA_LOAD) + == MMU_OK) { + return physical; + } - return physical; + return -1; } void cpu_load_tlb(CPUSH4State * env) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index d63a269aef..0ac46ddd91 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -9,6 +9,7 @@ ENV PACKAGES \ alsa-lib-dev \ bash \ binutils \ + ccache \ coreutils \ curl-dev \ g++ \ @@ -39,6 +40,7 @@ ENV PACKAGES \ pulseaudio-dev \ python3 \ py3-sphinx \ + py3-sphinx_rtd_theme \ shadow \ snappy-dev \ spice-dev \ diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker index d034acbd25..63cf835ec5 100644 --- a/tests/docker/dockerfiles/debian10.docker +++ b/tests/docker/dockerfiles/debian10.docker @@ -32,6 +32,7 @@ RUN apt update && \ psmisc \ python3 \ python3-sphinx \ + python3-sphinx-rtd-theme \ $(apt-get -s build-dep --arch-only qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) ENV FEATURES docs diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index 966072c08e..66cdb06c19 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -1,6 +1,7 @@ FROM fedora:33 ENV PACKAGES \ bzip2 \ + ccache \ diffutils \ findutils \ gcc \ diff --git a/tests/docker/dockerfiles/fedora-win32-cross.docker b/tests/docker/dockerfiles/fedora-win32-cross.docker index 81b5659e9c..3733df63e9 100644 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ b/tests/docker/dockerfiles/fedora-win32-cross.docker @@ -4,6 +4,7 @@ FROM fedora:33 ENV PACKAGES \ bc \ bzip2 \ + ccache \ diffutils \ findutils \ gcc \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index bcb428e724..2564ce4979 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -4,6 +4,7 @@ FROM fedora:33 ENV PACKAGES \ bc \ bzip2 \ + ccache \ diffutils \ findutils \ gcc \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 915fdc1845..d8fa16372d 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -92,6 +92,7 @@ ENV PACKAGES \ python3-pillow \ python3-pip \ python3-sphinx \ + python3-sphinx_rtd_theme \ python3-virtualenv \ rdma-core-devel \ SDL2-devel \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 0e64893e4a..f7e1cbfbe6 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -5,6 +5,7 @@ ENV PACKAGES \ bc \ brlapi-devel \ bzip2 \ + ccache \ cyrus-sasl-devel \ gcc \ gcc-c++ \ diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index b5ef7a8198..98a527361c 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -63,6 +63,7 @@ ENV PACKAGES \ ninja-build \ python3-yaml \ python3-sphinx \ + python3-sphinx-rtd-theme \ sparse \ xfslibs-dev RUN apt-get update && \ diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker index 9b0a19ba5e..c0d3642507 100644 --- a/tests/docker/dockerfiles/ubuntu1804.docker +++ b/tests/docker/dockerfiles/ubuntu1804.docker @@ -48,6 +48,7 @@ ENV PACKAGES \ make \ python3-yaml \ python3-sphinx \ + python3-sphinx-rtd-theme \ ninja-build \ sparse \ xfslibs-dev diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index 9750016e51..f1e0ebad49 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -58,6 +58,7 @@ ENV PACKAGES flex bison \ python3-pil \ python3-pip \ python3-sphinx \ + python3-sphinx-rtd-theme \ python3-venv \ python3-yaml \ rpm2cpio \ diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index 4ba5e1d2d4..c24baf8535 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -14,6 +14,7 @@ #include <math.h> #include <fenv.h> #include "qemu/timer.h" +#include "qemu/int128.h" #include "fpu/softfloat.h" /* amortize the computation of random inputs */ @@ -50,8 +51,10 @@ static const char * const op_names[] = { enum precision { PREC_SINGLE, PREC_DOUBLE, + PREC_QUAD, PREC_FLOAT32, PREC_FLOAT64, + PREC_FLOAT128, PREC_MAX_NR, }; @@ -89,6 +92,7 @@ union fp { double d; float32 f32; float64 f64; + float128 f128; uint64_t u64; }; @@ -113,6 +117,10 @@ struct op_desc { static uint64_t random_ops[MAX_OPERANDS] = { SEED_A, SEED_B, SEED_C, }; + +static float128 random_quad_ops[MAX_OPERANDS] = { + {SEED_A, SEED_B}, {SEED_B, SEED_C}, {SEED_C, SEED_A}, +}; static float_status soft_status; static enum precision precision; static enum op operation; @@ -141,25 +149,45 @@ static void update_random_ops(int n_ops, enum precision prec) int i; for (i = 0; i < n_ops; i++) { - uint64_t r = random_ops[i]; switch (prec) { case PREC_SINGLE: case PREC_FLOAT32: + { + uint64_t r = random_ops[i]; do { r = xorshift64star(r); } while (!float32_is_normal(r)); + random_ops[i] = r; break; + } case PREC_DOUBLE: case PREC_FLOAT64: + { + uint64_t r = random_ops[i]; do { r = xorshift64star(r); } while (!float64_is_normal(r)); + random_ops[i] = r; break; + } + case PREC_QUAD: + case PREC_FLOAT128: + { + float128 r = random_quad_ops[i]; + uint64_t hi = r.high; + uint64_t lo = r.low; + do { + hi = xorshift64star(hi); + lo = xorshift64star(lo); + r = make_float128(hi, lo); + } while (!float128_is_normal(r)); + random_quad_ops[i] = r; + break; + } default: g_assert_not_reached(); } - random_ops[i] = r; } } @@ -184,6 +212,13 @@ static void fill_random(union fp *ops, int n_ops, enum precision prec, ops[i].f64 = float64_chs(ops[i].f64); } break; + case PREC_QUAD: + case PREC_FLOAT128: + ops[i].f128 = random_quad_ops[i]; + if (no_neg && float128_is_neg(ops[i].f128)) { + ops[i].f128 = float128_chs(ops[i].f128); + } + break; default: g_assert_not_reached(); } @@ -345,6 +380,41 @@ static void bench(enum precision prec, enum op op, int n_ops, bool no_neg) } } break; + case PREC_FLOAT128: + fill_random(ops, n_ops, prec, no_neg); + t0 = get_clock(); + for (i = 0; i < OPS_PER_ITER; i++) { + float128 a = ops[0].f128; + float128 b = ops[1].f128; + float128 c = ops[2].f128; + + switch (op) { + case OP_ADD: + res.f128 = float128_add(a, b, &soft_status); + break; + case OP_SUB: + res.f128 = float128_sub(a, b, &soft_status); + break; + case OP_MUL: + res.f128 = float128_mul(a, b, &soft_status); + break; + case OP_DIV: + res.f128 = float128_div(a, b, &soft_status); + break; + case OP_FMA: + res.f128 = float128_muladd(a, b, c, 0, &soft_status); + break; + case OP_SQRT: + res.f128 = float128_sqrt(a, &soft_status); + break; + case OP_CMP: + res.u64 = float128_compare_quiet(a, b, &soft_status); + break; + default: + g_assert_not_reached(); + } + } + break; default: g_assert_not_reached(); } @@ -369,7 +439,8 @@ static void bench(enum precision prec, enum op op, int n_ops, bool no_neg) GEN_BENCH(bench_ ## opname ## _float, float, PREC_SINGLE, op, n_ops) \ GEN_BENCH(bench_ ## opname ## _double, double, PREC_DOUBLE, op, n_ops) \ GEN_BENCH(bench_ ## opname ## _float32, float32, PREC_FLOAT32, op, n_ops) \ - GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops) + GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _float128, float128, PREC_FLOAT128, op, n_ops) GEN_BENCH_ALL_TYPES(add, OP_ADD, 2) GEN_BENCH_ALL_TYPES(sub, OP_SUB, 2) @@ -383,7 +454,8 @@ GEN_BENCH_ALL_TYPES(cmp, OP_CMP, 2) GEN_BENCH_NO_NEG(bench_ ## name ## _float, float, PREC_SINGLE, op, n) \ GEN_BENCH_NO_NEG(bench_ ## name ## _double, double, PREC_DOUBLE, op, n) \ GEN_BENCH_NO_NEG(bench_ ## name ## _float32, float32, PREC_FLOAT32, op, n) \ - GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n) + GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float128, float128, PREC_FLOAT128, op, n) GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1) #undef GEN_BENCH_ALL_TYPES_NO_NEG @@ -397,6 +469,7 @@ GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1) [PREC_DOUBLE] = bench_ ## opname ## _double, \ [PREC_FLOAT32] = bench_ ## opname ## _float32, \ [PREC_FLOAT64] = bench_ ## opname ## _float64, \ + [PREC_FLOAT128] = bench_ ## opname ## _float128, \ } static const bench_func_t bench_funcs[OP_MAX_NR][PREC_MAX_NR] = { @@ -445,7 +518,7 @@ static void usage_complete(int argc, char *argv[]) fprintf(stderr, " -h = show this help message.\n"); fprintf(stderr, " -o = floating point operation (%s). Default: %s\n", op_list, op_names[0]); - fprintf(stderr, " -p = floating point precision (single, double). " + fprintf(stderr, " -p = floating point precision (single, double, quad[soft only]). " "Default: single\n"); fprintf(stderr, " -r = rounding mode (even, zero, down, up, tieaway). " "Default: even\n"); @@ -565,6 +638,8 @@ static void parse_args(int argc, char *argv[]) precision = PREC_SINGLE; } else if (!strcmp(optarg, "double")) { precision = PREC_DOUBLE; + } else if (!strcmp(optarg, "quad")) { + precision = PREC_QUAD; } else { fprintf(stderr, "Unsupported precision '%s'\n", optarg); exit(EXIT_FAILURE); @@ -608,6 +683,9 @@ static void parse_args(int argc, char *argv[]) case PREC_DOUBLE: precision = PREC_FLOAT64; break; + case PREC_QUAD: + precision = PREC_FLOAT128; + break; default: g_assert_not_reached(); } diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 5a4cad8c8b..ff131afbde 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -717,7 +717,7 @@ static void do_testfloat(int op, int rmode, bool exact) test_abz_f128(true_abz_f128M, subj_abz_f128M); break; case F128_MULADD: - not_implemented(); + test_abcz_f128(slow_f128M_mulAdd, qemu_f128M_mulAdd); break; case F128_SQRT: test_az_f128(slow_f128M_sqrt, qemu_f128M_sqrt); diff --git a/tests/fp/wrap.c.inc b/tests/fp/wrap.c.inc index 0cbd20013e..cb1bb77e4c 100644 --- a/tests/fp/wrap.c.inc +++ b/tests/fp/wrap.c.inc @@ -574,6 +574,18 @@ WRAP_MULADD(qemu_f32_mulAdd, float32_muladd, float32) WRAP_MULADD(qemu_f64_mulAdd, float64_muladd, float64) #undef WRAP_MULADD +static void qemu_f128M_mulAdd(const float128_t *ap, const float128_t *bp, + const float128_t *cp, float128_t *res) +{ + float128 a, b, c, ret; + + a = soft_to_qemu128(*ap); + b = soft_to_qemu128(*bp); + c = soft_to_qemu128(*cp); + ret = float128_muladd(a, b, c, 0, &qsf); + *res = qemu_to_soft128(ret); +} + #define WRAP_CMP16(name, func, retcond) \ static bool name(float16_t a, float16_t b) \ { \ diff --git a/tests/migration/guestperf/comparison.py b/tests/migration/guestperf/comparison.py index ba2edbe546..c03b3f6d7e 100644 --- a/tests/migration/guestperf/comparison.py +++ b/tests/migration/guestperf/comparison.py @@ -121,4 +121,18 @@ COMPARISONS = [ Scenario("compr-xbzrle-cache-50", compression_xbzrle=True, compression_xbzrle_cache=50), ]), + + + # Looking at effect of multifd with + # varying numbers of channels + Comparison("compr-multifd", scenarios = [ + Scenario("compr-multifd-channels-4", + multifd=True, multifd_channels=2), + Scenario("compr-multifd-channels-8", + multifd=True, multifd_channels=8), + Scenario("compr-multifd-channels-32", + multifd=True, multifd_channels=32), + Scenario("compr-multifd-channels-64", + multifd=True, multifd_channels=64), + ]), ] diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index 6b49aed579..208e095794 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -188,6 +188,22 @@ class Engine(object): 1024 * 1024 * 1024 / 100 * scenario._compression_xbzrle_cache)) + if scenario._multifd: + resp = src.command("migrate-set-capabilities", + capabilities = [ + { "capability": "multifd", + "state": True } + ]) + resp = src.command("migrate-set-parameters", + multifd_channels=scenario._multifd_channels) + resp = dst.command("migrate-set-capabilities", + capabilities = [ + { "capability": "multifd", + "state": True } + ]) + resp = dst.command("migrate-set-parameters", + multifd_channels=scenario._multifd_channels) + resp = src.command("migrate", uri=connect_uri) post_copy = False diff --git a/tests/migration/guestperf/scenario.py b/tests/migration/guestperf/scenario.py index 28ef36c26d..de70d9b2f5 100644 --- a/tests/migration/guestperf/scenario.py +++ b/tests/migration/guestperf/scenario.py @@ -29,7 +29,8 @@ class Scenario(object): post_copy=False, post_copy_iters=5, auto_converge=False, auto_converge_step=10, compression_mt=False, compression_mt_threads=1, - compression_xbzrle=False, compression_xbzrle_cache=10): + compression_xbzrle=False, compression_xbzrle_cache=10, + multifd=False, multifd_channels=2): self._name = name @@ -56,6 +57,9 @@ class Scenario(object): self._compression_xbzrle = compression_xbzrle self._compression_xbzrle_cache = compression_xbzrle_cache # percentage of guest RAM + self._multifd = multifd + self._multifd_channels = multifd_channels + def serialize(self): return { "name": self._name, @@ -73,6 +77,8 @@ class Scenario(object): "compression_mt_threads": self._compression_mt_threads, "compression_xbzrle": self._compression_xbzrle, "compression_xbzrle_cache": self._compression_xbzrle_cache, + "multifd": self._multifd, + "multifd_channels": self._multifd_channels, } @classmethod @@ -92,4 +98,6 @@ class Scenario(object): data["compression_mt"], data["compression_mt_threads"], data["compression_xbzrle"], - data["compression_xbzrle_cache"]) + data["compression_xbzrle_cache"], + data["multifd"], + data["multifd_channels"]) diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index f838888809..8a809e3dda 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -122,6 +122,11 @@ class Shell(BaseShell): parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true") parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int) + parser.add_argument("--multifd", dest="multifd", default=False, + action="store_true") + parser.add_argument("--multifd-channels", dest="multifd_channels", + default=2, type=int) + def get_scenario(self, args): return Scenario(name="perfreport", downtime=args.downtime, @@ -142,7 +147,10 @@ class Shell(BaseShell): compression_mt_threads=args.compression_mt_threads, compression_xbzrle=args.compression_xbzrle, - compression_xbzrle_cache=args.compression_xbzrle_cache) + compression_xbzrle_cache=args.compression_xbzrle_cache, + + multifd=args.multifd, + multifd_channels=args.multifd_channels) def run(self, argv): args = self._parser.parse_args(argv) diff --git a/tests/qemu-iotests/231 b/tests/qemu-iotests/231 index 0f66d0ca36..8e6c6447c1 100755 --- a/tests/qemu-iotests/231 +++ b/tests/qemu-iotests/231 @@ -55,6 +55,10 @@ _filter_conf() $QEMU_IMG info "json:{'file.driver':'rbd','file.filename':'rbd:rbd/bogus:conf=${BOGUS_CONF}'}" 2>&1 | _filter_conf $QEMU_IMG info "json:{'file.driver':'rbd','file.pool':'rbd','file.image':'bogus','file.conf':'${BOGUS_CONF}'}" 2>&1 | _filter_conf +# Regression test: the qemu-img invocation is expected to fail, but it should +# not seg fault the parser. +$QEMU_IMG create "rbd:rbd/aa\/bb:conf=${BOGUS_CONF}" 1M 2>&1 | _filter_conf + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/231.out b/tests/qemu-iotests/231.out index 579ba11c16..a785a6e859 100644 --- a/tests/qemu-iotests/231.out +++ b/tests/qemu-iotests/231.out @@ -1,9 +1,10 @@ QA output created by 231 -qemu-img: RBD options encoded in the filename as keyvalue pairs is deprecated. Future versions may cease to parse these options in the future. +qemu-img: warning: RBD options encoded in the filename as keyvalue pairs is deprecated unable to get monitor info from DNS SRV with service name: ceph-mon -no monitors specified to connect to. qemu-img: Could not open 'json:{'file.driver':'rbd','file.filename':'rbd:rbd/bogus:conf=BOGUS_CONF'}': error connecting: No such file or directory unable to get monitor info from DNS SRV with service name: ceph-mon -no monitors specified to connect to. qemu-img: Could not open 'json:{'file.driver':'rbd','file.pool':'rbd','file.image':'bogus','file.conf':'BOGUS_CONF'}': error connecting: No such file or directory +Formatting 'rbd:rbd/aa\/bb:conf=BOGUS_CONF', fmt=raw size=1048576 +unable to get monitor info from DNS SRV with service name: ceph-mon +qemu-img: rbd:rbd/aa\/bb:conf=BOGUS_CONF: error connecting: No such file or directory *** done diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out index e0982831ae..89ed25e506 100644 --- a/tests/qemu-iotests/240.out +++ b/tests/qemu-iotests/240.out @@ -15,7 +15,7 @@ {"return": {}} {"execute": "blockdev-del", "arguments": {"node-name": "hd0"}} {"return": {}} -==Attach two SCSI disks using the same block device and the same iothread== +.==Attach two SCSI disks using the same block device and the same iothread== {"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true, "read-zeroes": true}} {"return": {}} {"execute": "object-add", "arguments": {"id": "iothread0", "qom-type": "iothread"}} @@ -32,7 +32,7 @@ {"return": {}} {"execute": "blockdev-del", "arguments": {"node-name": "hd0"}} {"return": {}} -==Attach two SCSI disks using the same block device but different iothreads== +.==Attach two SCSI disks using the same block device but different iothreads== {"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true, "read-zeroes": true}} {"return": {}} {"execute": "object-add", "arguments": {"id": "iothread0", "qom-type": "iothread"}} @@ -55,7 +55,7 @@ {"return": {}} {"execute": "blockdev-del", "arguments": {"node-name": "hd0"}} {"return": {}} -==Attach a SCSI disks using the same block device as a NBD server== +.==Attach a SCSI disks using the same block device as a NBD server== {"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true, "read-zeroes": true}} {"return": {}} {"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}} @@ -68,7 +68,7 @@ {"return": {}} {"execute": "device_add", "arguments": {"drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} -.... +. ---------------------------------------------------------------------- Ran 4 tests diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out index 4b33dcaf5c..99c12f4f98 100644 --- a/tests/qemu-iotests/245.out +++ b/tests/qemu-iotests/245.out @@ -1,16 +1,16 @@ -{"execute": "job-finalize", "arguments": {"id": "commit0"}} +..{"execute": "job-finalize", "arguments": {"id": "commit0"}} {"return": {}} {"data": {"id": "commit0", "type": "commit"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "commit0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"execute": "job-finalize", "arguments": {"id": "stream0"}} +...{"execute": "job-finalize", "arguments": {"id": "stream0"}} {"return": {}} {"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"execute": "job-finalize", "arguments": {"id": "stream0"}} +.{"execute": "job-finalize", "arguments": {"id": "stream0"}} {"return": {}} {"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -..................... +............... ---------------------------------------------------------------------- Ran 21 tests diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264 index 4f96825a22..bc431d1a19 100755 --- a/tests/qemu-iotests/264 +++ b/tests/qemu-iotests/264 @@ -95,7 +95,7 @@ class TestNbdReconnect(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) def cancel_job(self): - result = self.vm.qmp('block-job-cancel', device='drive0') + result = self.vm.qmp('block-job-cancel', device='drive0', force=True) self.assert_qmp(result, 'return', {}) start_t = time.time() diff --git a/tests/qemu-iotests/295.out b/tests/qemu-iotests/295.out index ad34b2ca2c..5ff91f116c 100644 --- a/tests/qemu-iotests/295.out +++ b/tests/qemu-iotests/295.out @@ -4,7 +4,7 @@ {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} {"return": {}} -{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +.{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} {"return": {}} @@ -13,7 +13,7 @@ Job failed: Invalid password, cannot unlock any keyslot {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} {"return": {}} -{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} +.{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job_add_key"}} {"return": {}} @@ -33,7 +33,7 @@ Job failed: All the active keyslots match the (old) password that was given and {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}} {"return": {}} -... +. ---------------------------------------------------------------------- Ran 3 tests diff --git a/tests/qemu-iotests/296.out b/tests/qemu-iotests/296.out index cb2859a15c..6c69735604 100644 --- a/tests/qemu-iotests/296.out +++ b/tests/qemu-iotests/296.out @@ -13,7 +13,7 @@ Job failed: Failed to get shared "consistent read" lock qemu-img: Failed to get shared "consistent read" lock Is another process using the image [TEST_DIR/test.img]? -Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 +.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 Job failed: Block node is read-only {"execute": "job-dismiss", "arguments": {"id": "job0"}} @@ -26,15 +26,15 @@ Job failed: Failed to get shared "consistent read" lock {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 +.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 {"return": {}} {"error": {"class": "GenericError", "desc": "Failed to get \"write\" lock"}} -Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 +.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 {"return": {}} {"return": {}} -.... +. ---------------------------------------------------------------------- Ran 4 tests diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 08f51366f1..2dd529eb75 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -19,6 +19,9 @@ import os import sys import argparse +import shutil +from pathlib import Path + from findtests import TestFinder from testenv import TestEnv from testrunner import TestRunner @@ -100,7 +103,7 @@ def make_argparser() -> argparse.ArgumentParser: 'rerun failed ./check command, starting from the ' 'middle of the process.') g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*', - help='tests to run') + help='tests to run, or "--" followed by a command') return p @@ -113,6 +116,20 @@ if __name__ == '__main__': imgopts=args.imgopts, misalign=args.misalign, debug=args.debug, valgrind=args.valgrind) + if len(sys.argv) > 1 and sys.argv[-len(args.tests)-1] == '--': + if not args.tests: + sys.exit("missing command after '--'") + cmd = args.tests + env.print_env() + exec_pathstr = shutil.which(cmd[0]) + if exec_pathstr is None: + sys.exit('command not found: ' + cmd[0]) + exec_path = Path(exec_pathstr).resolve() + cmd[0] = str(exec_path) + full_env = env.prepare_subprocess(cmd) + os.chdir(exec_path.parent) + os.execve(cmd[0], cmd, full_env) + testfinder = TestFinder(test_dir=env.source_iotests) groups = args.groups.split(',') if args.groups else None diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 5af0182895..777fa2ec0e 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -20,7 +20,6 @@ import atexit import bz2 from collections import OrderedDict import faulthandler -import io import json import logging import os @@ -32,7 +31,7 @@ import subprocess import sys import time from typing import (Any, Callable, Dict, Iterable, - List, Optional, Sequence, Tuple, TypeVar) + List, Optional, Sequence, TextIO, Tuple, Type, TypeVar) import unittest from contextlib import contextmanager @@ -113,15 +112,14 @@ def qemu_tool_pipe_and_status(tool: str, args: Sequence[str], Run a tool and return both its output and its exit code """ stderr = subprocess.STDOUT if connect_stderr else None - subp = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=stderr, - universal_newlines=True) - output = subp.communicate()[0] - if subp.returncode < 0: - cmd = ' '.join(args) - sys.stderr.write(f'{tool} received signal {-subp.returncode}: {cmd}\n') - return (output, subp.returncode) + with subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=stderr, universal_newlines=True) as subp: + output = subp.communicate()[0] + if subp.returncode < 0: + cmd = ' '.join(args) + sys.stderr.write(f'{tool} received signal \ + {-subp.returncode}: {cmd}\n') + return (output, subp.returncode) def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]: """ @@ -237,6 +235,9 @@ def qemu_io_silent_check(*args): class QemuIoInteractive: def __init__(self, *args): self.args = qemu_io_args_no_fmt + list(args) + # We need to keep the Popen objext around, and not + # close it immediately. Therefore, disable the pylint check: + # pylint: disable=consider-using-with self._p = subprocess.Popen(self.args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, @@ -310,22 +311,22 @@ def qemu_nbd_popen(*args): cmd.extend(args) log('Start NBD server') - p = subprocess.Popen(cmd) - try: - while not os.path.exists(pid_file): - if p.poll() is not None: - raise RuntimeError( - "qemu-nbd terminated with exit code {}: {}" - .format(p.returncode, ' '.join(cmd))) - - time.sleep(0.01) - yield - finally: - if os.path.exists(pid_file): - os.remove(pid_file) - log('Kill NBD server') - p.kill() - p.wait() + with subprocess.Popen(cmd) as p: + try: + while not os.path.exists(pid_file): + if p.poll() is not None: + raise RuntimeError( + "qemu-nbd terminated with exit code {}: {}" + .format(p.returncode, ' '.join(cmd))) + + time.sleep(0.01) + yield + finally: + if os.path.exists(pid_file): + os.remove(pid_file) + log('Kill NBD server') + p.kill() + p.wait() def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): '''Return True if two image files are identical''' @@ -334,13 +335,12 @@ def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): def create_image(name, size): '''Create a fully-allocated raw image with sector markers''' - file = open(name, 'wb') - i = 0 - while i < size: - sector = struct.pack('>l504xl', i // 512, i // 512) - file.write(sector) - i = i + 512 - file.close() + with open(name, 'wb') as file: + i = 0 + while i < size: + sector = struct.pack('>l504xl', i // 512, i // 512) + file.write(sector) + i = i + 512 def image_size(img): '''Return image's virtual size''' @@ -1271,37 +1271,54 @@ def skip_if_user_is_root(func): return func(*args, **kwargs) return func_wrapper -def execute_unittest(debug=False): +# We need to filter out the time taken from the output so that +# qemu-iotest can reliably diff the results against master output, +# and hide skipped tests from the reference output. + +class ReproducibleTestResult(unittest.TextTestResult): + def addSkip(self, test, reason): + # Same as TextTestResult, but print dot instead of "s" + unittest.TestResult.addSkip(self, test, reason) + if self.showAll: + self.stream.writeln("skipped {0!r}".format(reason)) + elif self.dots: + self.stream.write(".") + self.stream.flush() + +class ReproducibleStreamWrapper: + def __init__(self, stream: TextIO): + self.stream = stream + + def __getattr__(self, attr): + if attr in ('stream', '__getstate__'): + raise AttributeError(attr) + return getattr(self.stream, attr) + + def write(self, arg=None): + arg = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', arg) + arg = re.sub(r' \(skipped=\d+\)', r'', arg) + self.stream.write(arg) + +class ReproducibleTestRunner(unittest.TextTestRunner): + def __init__(self, stream: Optional[TextIO] = None, + resultclass: Type[unittest.TestResult] = ReproducibleTestResult, + **kwargs: Any) -> None: + rstream = ReproducibleStreamWrapper(stream or sys.stdout) + super().__init__(stream=rstream, # type: ignore + descriptions=True, + resultclass=resultclass, + **kwargs) + +def execute_unittest(argv: List[str], debug: bool = False) -> None: """Executes unittests within the calling module.""" - verbosity = 2 if debug else 1 - - if debug: - output = sys.stdout - else: - # We need to filter out the time taken from the output so that - # qemu-iotest can reliably diff the results against master output. - output = io.StringIO() - - runner = unittest.TextTestRunner(stream=output, descriptions=True, - verbosity=verbosity) - try: - # unittest.main() will use sys.exit(); so expect a SystemExit - # exception - unittest.main(testRunner=runner) - finally: - # We need to filter out the time taken from the output so that - # qemu-iotest can reliably diff the results against master output. - if not debug: - out = output.getvalue() - out = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', out) - - # Hide skipped tests from the reference output - out = re.sub(r'OK \(skipped=\d+\)', 'OK', out) - out_first_line, out_rest = out.split('\n', 1) - out = out_first_line.replace('s', '.') + '\n' + out_rest - - sys.stderr.write(out) + # Some tests have warnings, especially ResourceWarnings for unclosed + # files and sockets. Ignore them for now to ensure reproducibility of + # the test output. + unittest.main(argv=argv, + testRunner=ReproducibleTestRunner, + verbosity=2 if debug else 1, + warnings=None if sys.warnoptions else 'ignore') def execute_setup_common(supported_fmts: Sequence[str] = (), supported_platforms: Sequence[str] = (), @@ -1338,7 +1355,7 @@ def execute_test(*args, test_function=None, **kwargs): debug = execute_setup_common(*args, **kwargs) if not test_function: - execute_unittest(debug) + execute_unittest(sys.argv, debug) else: test_function() diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index 7a6c0a9474..f2c0b522ac 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -19,6 +19,9 @@ disable=invalid-name, too-many-public-methods, # pylint warns about Optional[] etc. as unsubscriptable in 3.9 unsubscriptable-object, + # Sometimes we need to disable a newly introduced pylint warning. + # Doing so should not produce a warning in older versions of pylint. + bad-option-value, # These are temporary, and should be removed: missing-docstring, too-many-return-statements, diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 6d27712617..0c3fe75636 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -25,7 +25,7 @@ import collections import random import subprocess import glob -from typing import Dict, Any, Optional, ContextManager +from typing import List, Dict, Any, Optional, ContextManager def isxfile(path: str) -> bool: @@ -74,6 +74,21 @@ class TestEnv(ContextManager['TestEnv']): 'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX', 'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_'] + def prepare_subprocess(self, args: List[str]) -> Dict[str, str]: + if self.debug: + args.append('-d') + + with open(args[0], encoding="utf-8") as f: + try: + if f.readline().rstrip() == '#!/usr/bin/env python3': + args.insert(0, self.python) + except UnicodeDecodeError: # binary test? for future. + pass + + os_env = os.environ.copy() + os_env.update(self.get_env()) + return os_env + def get_env(self) -> Dict[str, str]: env = {} for v in self.env_variables: @@ -105,7 +120,7 @@ class TestEnv(ContextManager['TestEnv']): try: self.sock_dir = os.environ['SOCK_DIR'] self.tmp_sock_dir = False - Path(self.test_dir).mkdir(parents=True, exist_ok=True) + Path(self.sock_dir).mkdir(parents=True, exist_ok=True) except KeyError: self.sock_dir = tempfile.mkdtemp() self.tmp_sock_dir = True @@ -269,7 +284,8 @@ IMGPROTO -- {IMGPROTO} PLATFORM -- {platform} TEST_DIR -- {TEST_DIR} SOCK_DIR -- {SOCK_DIR} -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}""" +SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER} +""" args = collections.defaultdict(str, self.get_env()) diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 1fc61fcaa3..4a6ec421ed 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -129,7 +129,6 @@ class TestRunner(ContextManager['TestRunner']): def __init__(self, env: TestEnv, makecheck: bool = False, color: str = 'auto') -> None: self.env = env - self.test_run_env = self.env.get_env() self.makecheck = makecheck self.last_elapsed = LastElapsedTime('.last-elapsed-cache', env) @@ -243,32 +242,21 @@ class TestRunner(ContextManager['TestRunner']): silent_unlink(p) args = [str(f_test.resolve())] - if self.env.debug: - args.append('-d') - - with f_test.open(encoding="utf-8") as f: - try: - if f.readline().rstrip() == '#!/usr/bin/env python3': - args.insert(0, self.env.python) - except UnicodeDecodeError: # binary test? for future. - pass - - env = os.environ.copy() - env.update(self.test_run_env) + env = self.env.prepare_subprocess(args) t0 = time.time() with f_bad.open('w', encoding="utf-8") as f: - proc = subprocess.Popen(args, cwd=str(f_test.parent), env=env, - stdout=f, stderr=subprocess.STDOUT) - try: - proc.wait() - except KeyboardInterrupt: - proc.terminate() - proc.wait() - return TestResult(status='not run', - description='Interrupted by user', - interrupted=True) - ret = proc.returncode + with subprocess.Popen(args, cwd=str(f_test.parent), env=env, + stdout=f, stderr=subprocess.STDOUT) as proc: + try: + proc.wait() + except KeyboardInterrupt: + proc.terminate() + proc.wait() + return TestResult(status='not run', + description='Interrupted by user', + interrupted=True) + ret = proc.returncode elapsed = round(time.time() - t0, 1) @@ -328,7 +316,6 @@ class TestRunner(ContextManager['TestRunner']): if not self.makecheck: self.env.print_env() - print() test_field_width = max(len(os.path.basename(t)) for t in tests) + 2 diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 5e1954852e..8073ccc205 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1491,14 +1491,14 @@ static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, char *iso; int fd; AHCIOpts opts = { - .size = (ATAPI_SECTOR_SIZE * nsectors), + .size = ((uint64_t)ATAPI_SECTOR_SIZE * nsectors), .atapi = true, .atapi_dma = dma, .post_cb = ahci_cb_cmp_buff, .set_bcl = override_bcl, .bcl = bcl, }; - uint64_t iso_size = ATAPI_SECTOR_SIZE * (nsectors + 1); + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * (nsectors + 1); /* Prepare ISO and fill 'tx' buffer */ fd = prepare_iso(iso_size, &tx, &iso); diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index a42207d416..8492f02a9c 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -98,7 +98,8 @@ static void bt_wait_b_busy(void) { unsigned int count = 1000; while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) { - g_assert(--count != 0); + --count; + g_assert(count != 0); usleep(100); } } @@ -107,7 +108,8 @@ static void bt_wait_b2h_atn(void) { unsigned int count = 1000; while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) { - g_assert(--count != 0); + --count; + g_assert(count != 0); usleep(100); } } diff --git a/tests/qtest/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c index fc0a918c8d..afc24dd3e4 100644 --- a/tests/qtest/ipmi-kcs-test.c +++ b/tests/qtest/ipmi-kcs-test.c @@ -73,7 +73,8 @@ static void kcs_wait_ibf(void) { unsigned int count = 1000; while (IPMI_KCS_CMDREG_GET_IBF() != 0) { - g_assert(--count != 0); + --count; + g_assert(count != 0); } } diff --git a/tests/qtest/libqos/qgraph.c b/tests/qtest/libqos/qgraph.c index b3b1a31f81..d1dc491930 100644 --- a/tests/qtest/libqos/qgraph.c +++ b/tests/qtest/libqos/qgraph.c @@ -844,7 +844,7 @@ void qos_dump_graph(void) } qos_printf_literal("type=%d cmd_line='%s' [%s]\n", node->type, node->command_line, - node->available ? "available" : "UNAVAILBLE" + node->available ? "available" : "UNAVAILABLE" ); } g_list_free(keys); diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 71e359efcd..825b13a44c 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -907,7 +907,14 @@ const char *qtest_get_arch(void) if (!end) { fprintf(stderr, "Can't determine architecture from binary name.\n"); - abort(); + exit(1); + } + + if (!strstr(qemu, "-system-")) { + fprintf(stderr, "QTEST_QEMU_BINARY must end with *-system-<arch> " + "where 'arch' is the target\narchitecture (x86_64, aarch64, " + "etc).\n"); + exit(1); } return end + 1; diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 3a711bb492..2b028df687 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -110,13 +110,12 @@ static void init_bootfile(const char *bootpath, void *content, size_t len) */ static void wait_for_serial(const char *side) { - char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); + g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); FILE *serialfile = fopen(serialpath, "r"); const char *arch = qtest_get_arch(); int started = (strcmp(side, "src_serial") == 0 && strcmp(arch, "ppc64") == 0) ? 0 : 1; - g_free(serialpath); do { int readvalue = fgetc(serialfile); @@ -274,10 +273,9 @@ static void check_guests_ram(QTestState *who) static void cleanup(const char *filename) { - char *path = g_strdup_printf("%s/%s", tmpfs, filename); + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); unlink(path); - g_free(path); } static char *SocketAddress_to_str(SocketAddress *addr) @@ -374,11 +372,8 @@ static char *migrate_get_parameter_str(QTestState *who, static void migrate_check_parameter_str(QTestState *who, const char *parameter, const char *value) { - char *result; - - result = migrate_get_parameter_str(who, parameter); + g_autofree char *result = migrate_get_parameter_str(who, parameter); g_assert_cmpstr(result, ==, value); - g_free(result); } static void migrate_set_parameter_str(QTestState *who, const char *parameter, @@ -495,12 +490,14 @@ static void migrate_start_destroy(MigrateStart *args) static int test_migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { - gchar *arch_source, *arch_target; - gchar *cmd_source, *cmd_target; + g_autofree gchar *arch_source = NULL; + g_autofree gchar *arch_target = NULL; + g_autofree gchar *cmd_source = NULL; + g_autofree gchar *cmd_target = NULL; const gchar *ignore_stderr; - char *bootpath = NULL; - char *shmem_opts; - char *shmem_path; + g_autofree char *bootpath = NULL; + g_autofree char *shmem_opts = NULL; + g_autofree char *shmem_path = NULL; const char *arch = qtest_get_arch(); const char *machine_opts = NULL; const char *memory_size; @@ -559,8 +556,6 @@ static int test_migrate_start(QTestState **from, QTestState **to, g_assert_not_reached(); } - g_free(bootpath); - if (!getenv("QTEST_LOG") && args->hide_stderr) { ignore_stderr = "2>/dev/null"; } else { @@ -588,11 +583,9 @@ static int test_migrate_start(QTestState **from, QTestState **to, memory_size, tmpfs, arch_source, shmem_opts, args->opts_source, ignore_stderr); - g_free(arch_source); if (!args->only_target) { *from = qtest_init(cmd_source); } - g_free(cmd_source); cmd_target = g_strdup_printf("-accel kvm -accel tcg%s%s " "-name target,debug-threads=on " @@ -605,18 +598,14 @@ static int test_migrate_start(QTestState **from, QTestState **to, memory_size, tmpfs, uri, arch_target, shmem_opts, args->opts_target, ignore_stderr); - g_free(arch_target); *to = qtest_init(cmd_target); - g_free(cmd_target); - g_free(shmem_opts); /* * Remove shmem file immediately to avoid memory leak in test failed case. * It's valid becase QEMU has already opened this file */ if (args->use_shmem) { unlink(shmem_path); - g_free(shmem_path); } out: @@ -662,7 +651,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, QTestState **to_ptr, MigrateStart *args) { - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; if (test_migrate_start(&from, &to, uri, args)) { @@ -684,7 +673,6 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, wait_for_serial("src_serial"); migrate_qmp(from, uri, "{}"); - g_free(uri); wait_for_migration_pass(from); @@ -724,7 +712,7 @@ static void test_postcopy_recovery(void) { MigrateStart *args = migrate_start_new(); QTestState *from, *to; - char *uri; + g_autofree char *uri = NULL; args->hide_stderr = true; @@ -775,7 +763,6 @@ static void test_postcopy_recovery(void) (const char * []) { "failed", "active", "completed", NULL }); migrate_qmp(from, uri, "{'resume': true}"); - g_free(uri); /* Restore the postcopy bandwidth to unlimited */ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); @@ -800,7 +787,7 @@ static void test_baddest(void) static void test_precopy_unix(void) { - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); MigrateStart *args = migrate_start_new(); QTestState *from, *to; @@ -836,14 +823,13 @@ static void test_precopy_unix(void) wait_for_migration_complete(from); test_migrate_end(from, to, true); - g_free(uri); } #if 0 /* Currently upset on aarch64 TCG */ static void test_ignore_shared(void) { - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { @@ -873,7 +859,6 @@ static void test_ignore_shared(void) g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); test_migrate_end(from, to, true); - g_free(uri); } #endif @@ -898,8 +883,8 @@ static void test_xbzrle(const char *uri) migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - migrate_set_capability(from, "xbzrle", "true"); - migrate_set_capability(to, "xbzrle", "true"); + migrate_set_capability(from, "xbzrle", true); + migrate_set_capability(to, "xbzrle", true); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -925,16 +910,15 @@ static void test_xbzrle(const char *uri) static void test_xbzrle_unix(void) { - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); test_xbzrle(uri); - g_free(uri); } static void test_precopy_tcp(void) { MigrateStart *args = migrate_start_new(); - char *uri; + g_autofree char *uri = NULL; QTestState *from, *to; if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) { @@ -971,7 +955,6 @@ static void test_precopy_tcp(void) wait_for_migration_complete(from); test_migrate_end(from, to, true); - g_free(uri); } static void test_migrate_fd_proto(void) @@ -1060,7 +1043,7 @@ static void test_migrate_fd_proto(void) static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; if (test_migrate_start(&from, &to, uri, args)) { @@ -1088,7 +1071,6 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) } test_migrate_end(from, to, false); - g_free(uri); } static void test_validate_uuid(void) @@ -1136,7 +1118,7 @@ static void test_validate_uuid_dst_not_set(void) static void test_migrate_auto_converge(void) { - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); MigrateStart *args = migrate_start_new(); QTestState *from, *to; int64_t remaining, percentage; @@ -1214,7 +1196,6 @@ static void test_migrate_auto_converge(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - g_free(uri); test_migrate_end(from, to, true); } @@ -1224,7 +1205,7 @@ static void test_multifd_tcp(const char *method) MigrateStart *args = migrate_start_new(); QTestState *from, *to; QDict *rsp; - char *uri; + g_autofree char *uri = NULL; if (test_migrate_start(&from, &to, "defer", args)) { return; @@ -1246,8 +1227,8 @@ static void test_multifd_tcp(const char *method) migrate_set_parameter_str(from, "multifd-compression", method); migrate_set_parameter_str(to, "multifd-compression", method); - migrate_set_capability(from, "multifd", "true"); - migrate_set_capability(to, "multifd", "true"); + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," @@ -1273,7 +1254,6 @@ static void test_multifd_tcp(const char *method) wait_for_serial("dest_serial"); wait_for_migration_complete(from); test_migrate_end(from, to, true); - g_free(uri); } static void test_multifd_tcp_none(void) @@ -1309,7 +1289,7 @@ static void test_multifd_tcp_cancel(void) MigrateStart *args = migrate_start_new(); QTestState *from, *to, *to2; QDict *rsp; - char *uri; + g_autofree char *uri = NULL; args->hide_stderr = true; @@ -1330,8 +1310,8 @@ static void test_multifd_tcp_cancel(void) migrate_set_parameter_int(from, "multifd-channels", 16); migrate_set_parameter_int(to, "multifd-channels", 16); - migrate_set_capability(from, "multifd", "true"); - migrate_set_capability(to, "multifd", "true"); + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," @@ -1358,7 +1338,7 @@ static void test_multifd_tcp_cancel(void) migrate_set_parameter_int(to2, "multifd-channels", 16); - migrate_set_capability(to2, "multifd", "true"); + migrate_set_capability(to2, "multifd", true); /* Start incoming migration from the 1st socket */ rsp = wait_command(to2, "{ 'execute': 'migrate-incoming'," @@ -1387,7 +1367,6 @@ static void test_multifd_tcp_cancel(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); test_migrate_end(from, to2, true); - g_free(uri); } int main(int argc, char **argv) diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c index bd15a1c294..a54fd70d27 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -201,7 +201,7 @@ static int pwm_module_index(const PWMModule *module) { ptrdiff_t diff = module - pwm_module_list; - g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list)); + g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list)); return diff; } @@ -211,7 +211,7 @@ static int pwm_index(const PWM *pwm) { ptrdiff_t diff = pwm - pwm_list; - g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list)); + g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_list)); return diff; } diff --git a/tests/qtest/rtc-test.c b/tests/qtest/rtc-test.c index 402ce2c609..8126ab1bdb 100644 --- a/tests/qtest/rtc-test.c +++ b/tests/qtest/rtc-test.c @@ -686,7 +686,7 @@ static void periodic_timer(void) int main(int argc, char **argv) { - QTestState *s = NULL; + QTestState *s; int ret; g_test_init(&argc, &argv, NULL); @@ -712,9 +712,7 @@ int main(int argc, char **argv) ret = g_test_run(); - if (s) { - qtest_quit(s); - } + qtest_quit(s); return ret; } diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c index b70cc32d60..3a40ff3f96 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -289,6 +289,6 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, *dst_qemu = qtest_init(dst_qemu_args); - free(src_qemu_args); - free(dst_qemu_args); + g_free(src_qemu_args); + g_free(dst_qemu_args); } diff --git a/tests/unit/test-write-threshold.c b/tests/unit/test-write-threshold.c index fc1c45a2eb..0158e4637a 100644 --- a/tests/unit/test-write-threshold.c +++ b/tests/unit/test-write-threshold.c @@ -7,117 +7,41 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" #include "block/block_int.h" #include "block/write-threshold.h" -static void test_threshold_not_set_on_init(void) -{ - uint64_t res; - BlockDriverState bs; - memset(&bs, 0, sizeof(bs)); - - g_assert(!bdrv_write_threshold_is_set(&bs)); - - res = bdrv_write_threshold_get(&bs); - g_assert_cmpint(res, ==, 0); -} - -static void test_threshold_set_get(void) -{ - uint64_t threshold = 4 * 1024 * 1024; - uint64_t res; - BlockDriverState bs; - memset(&bs, 0, sizeof(bs)); - - bdrv_write_threshold_set(&bs, threshold); - - g_assert(bdrv_write_threshold_is_set(&bs)); - - res = bdrv_write_threshold_get(&bs); - g_assert_cmpint(res, ==, threshold); -} - -static void test_threshold_multi_set_get(void) -{ - uint64_t threshold1 = 4 * 1024 * 1024; - uint64_t threshold2 = 15 * 1024 * 1024; - uint64_t res; - BlockDriverState bs; - memset(&bs, 0, sizeof(bs)); - - bdrv_write_threshold_set(&bs, threshold1); - bdrv_write_threshold_set(&bs, threshold2); - res = bdrv_write_threshold_get(&bs); - g_assert_cmpint(res, ==, threshold2); -} - static void test_threshold_not_trigger(void) { - uint64_t amount = 0; uint64_t threshold = 4 * 1024 * 1024; BlockDriverState bs; - BdrvTrackedRequest req; memset(&bs, 0, sizeof(bs)); - memset(&req, 0, sizeof(req)); - req.offset = 1024; - req.bytes = 1024; - - bdrv_check_request(req.offset, req.bytes, &error_abort); bdrv_write_threshold_set(&bs, threshold); - amount = bdrv_write_threshold_exceeded(&bs, &req); - g_assert_cmpuint(amount, ==, 0); + bdrv_write_threshold_check_write(&bs, 1024, 1024); + g_assert_cmpuint(bdrv_write_threshold_get(&bs), ==, threshold); } static void test_threshold_trigger(void) { - uint64_t amount = 0; uint64_t threshold = 4 * 1024 * 1024; BlockDriverState bs; - BdrvTrackedRequest req; memset(&bs, 0, sizeof(bs)); - memset(&req, 0, sizeof(req)); - req.offset = (4 * 1024 * 1024) - 1024; - req.bytes = 2 * 1024; - - bdrv_check_request(req.offset, req.bytes, &error_abort); bdrv_write_threshold_set(&bs, threshold); - amount = bdrv_write_threshold_exceeded(&bs, &req); - g_assert_cmpuint(amount, >=, 1024); + bdrv_write_threshold_check_write(&bs, threshold - 1024, 2 * 1024); + g_assert_cmpuint(bdrv_write_threshold_get(&bs), ==, 0); } -typedef struct TestStruct { - const char *name; - void (*func)(void); -} TestStruct; - int main(int argc, char **argv) { - size_t i; - TestStruct tests[] = { - { "/write-threshold/not-set-on-init", - test_threshold_not_set_on_init }, - { "/write-threshold/set-get", - test_threshold_set_get }, - { "/write-threshold/multi-set-get", - test_threshold_multi_set_get }, - { "/write-threshold/not-trigger", - test_threshold_not_trigger }, - { "/write-threshold/trigger", - test_threshold_trigger }, - { NULL, NULL } - }; - g_test_init(&argc, &argv, NULL); - for (i = 0; tests[i].name != NULL; i++) { - g_test_add_func(tests[i].name, tests[i].func); - } + g_test_add_func("/write-threshold/not-trigger", test_threshold_not_trigger); + g_test_add_func("/write-threshold/trigger", test_threshold_trigger); + return g_test_run(); } diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 1170f375a5..9efdbd8ffd 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -1024,9 +1024,9 @@ static int fv_create_listen_socket(struct fuse_session *se) if (se->vu_socket_group) { struct group *g = getgrnam(se->vu_socket_group); if (g) { - if (!chown(se->vu_socket_path, -1, g->gr_gid)) { + if (chown(se->vu_socket_path, -1, g->gr_gid) == -1) { fuse_log(FUSE_LOG_WARNING, - "vhost socket failed to set group to %s (%d)\n", + "vhost socket failed to set group to %s (%d): %m\n", se->vu_socket_group, g->gr_gid); } } diff --git a/util/compatfd.c b/util/compatfd.c index 174f394533..a8ec525c6c 100644 --- a/util/compatfd.c +++ b/util/compatfd.c @@ -72,14 +72,10 @@ static int qemu_signalfd_compat(const sigset_t *mask) QemuThread thread; int fds[2]; - info = malloc(sizeof(*info)); - if (info == NULL) { - errno = ENOMEM; - return -1; - } + info = g_malloc(sizeof(*info)); if (pipe(fds) == -1) { - free(info); + g_free(info); return -1; } diff --git a/util/cutils.c b/util/cutils.c index ee908486da..c9b91e7535 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -1055,5 +1055,5 @@ char *get_relocated_path(const char *dir) assert(G_IS_DIR_SEPARATOR(dir[-1])); g_string_append(result, dir - 1); } - return result->str; + return g_string_free(result, false); } diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 97dfa3fd57..911115b86e 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -459,51 +459,38 @@ fail_container: return ret; } -static void qemu_vfio_ram_block_added(RAMBlockNotifier *n, - void *host, size_t size) +static void qemu_vfio_ram_block_added(RAMBlockNotifier *n, void *host, + size_t size, size_t max_size) { QEMUVFIOState *s = container_of(n, QEMUVFIOState, ram_notifier); - trace_qemu_vfio_ram_block_added(s, host, size); - qemu_vfio_dma_map(s, host, size, false, NULL); + int ret; + + trace_qemu_vfio_ram_block_added(s, host, max_size); + ret = qemu_vfio_dma_map(s, host, max_size, false, NULL); + if (ret) { + error_report("qemu_vfio_dma_map(%p, %zu) failed: %s", host, max_size, + strerror(-ret)); + } } -static void qemu_vfio_ram_block_removed(RAMBlockNotifier *n, - void *host, size_t size) +static void qemu_vfio_ram_block_removed(RAMBlockNotifier *n, void *host, + size_t size, size_t max_size) { QEMUVFIOState *s = container_of(n, QEMUVFIOState, ram_notifier); if (host) { - trace_qemu_vfio_ram_block_removed(s, host, size); + trace_qemu_vfio_ram_block_removed(s, host, max_size); qemu_vfio_dma_unmap(s, host); } } -static int qemu_vfio_init_ramblock(RAMBlock *rb, void *opaque) -{ - void *host_addr = qemu_ram_get_host_addr(rb); - ram_addr_t length = qemu_ram_get_used_length(rb); - int ret; - QEMUVFIOState *s = opaque; - - if (!host_addr) { - return 0; - } - ret = qemu_vfio_dma_map(s, host_addr, length, false, NULL); - if (ret) { - fprintf(stderr, "qemu_vfio_init_ramblock: failed %p %" PRId64 "\n", - host_addr, (uint64_t)length); - } - return 0; -} - static void qemu_vfio_open_common(QEMUVFIOState *s) { qemu_mutex_init(&s->lock); s->ram_notifier.ram_block_added = qemu_vfio_ram_block_added; s->ram_notifier.ram_block_removed = qemu_vfio_ram_block_removed; - ram_block_notifier_add(&s->ram_notifier); s->low_water_mark = QEMU_VFIO_IOVA_MIN; s->high_water_mark = QEMU_VFIO_IOVA_MAX; - qemu_ram_foreach_block(qemu_vfio_init_ramblock, s); + ram_block_notifier_add(&s->ram_notifier); } /** |