v10.2.1 release

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEZKoqtTHVaQM2a/75gqpKJDselHgFAmmNxEkACgkQgqpKJDse
 lHg2gA/+MhKW6VuQGeOISkufRgYv2d8YdZqxV9u/iuqfCBniNoWfQ59oCMCy0lG7
 Pz/j8DQn3r8Da4EICxbuEAT9IIppMfzJNns60inm1mmp9jNvUQoT6M8pIWBC2CH3
 DKQygiwClFKRfmZuagyGUr213WThQ0lEmZCmJxP457zT2xA2ogEBfyIq0Qk2fYmN
 Z9pAfXiZ5bVDwk1PL9zhfyub8t09COp3kPWAZhVwzbDfkPXvdHy/hMXwz5tZ2COw
 e7PhA8R1IMEGXc6WOzE8c0eboSs+pU8R88b1SGZlTfxD3Cv7WGvji5unvsC1a5nZ
 FC6kVql/aDs56kso1NGVLZdCRwKP+9Qjej+w78W9wJI2lBodx3flTXv3XUdK+7Hz
 Tc8BJ/88oRkG3Az7vP7q+WSN2OsHumY7KENAeJE9BEEqiHqdpI2WoIG9pmCaRfrG
 7KNJtpVr6C8+nPHcudroLx0EhwQz1csRUWem0ko7pRECYOMe27/2dPvo6QNsng82
 sCy8k0GoV0DgrSRMjFX0ahi8AtKZ0xv2h71BWgNYtuI9USW7RY9aPEYlvvVVfUoa
 4Y0ZeR67+CyMJ36gGmDCuGHfYsJGzXft5m4gsEiHh5PEQIiytEV3GZ8+XS3syGfY
 XDR6CYSkFAhx68j8bBOHtwBSYTbOWPJ5vo14ZQ6BYlGGAq0KUp8=
 =X7O5
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQc1ZFRpfysT1VCnEeNmYZ3l6SIbwUCabiRVgAKCRCNmYZ3l6SI
 b+sPAQCmnnkEKBNVBNRnpje0dsIuOgoANrCm97vulk4siFDVqwD+KYnBILtUH/RP
 ZMlhIqKkK7T62a2wzt2bITXi4dCWLgw=
 =Za5B
 -----END PGP SIGNATURE-----

Merge tag 'v10.2.1' into cr16-wip

v10.2.1 release
This commit is contained in:
Jonas Bewig 2026-03-17 00:24:55 +01:00
commit c3d65261db
2322 changed files with 94070 additions and 43853 deletions

View file

@ -55,7 +55,7 @@ indent_size = 4
emacs_mode = perl emacs_mode = perl
# but user kernel "style" for imported scripts # but user kernel "style" for imported scripts
[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] [scripts/{get_maintainer.pl,checkpatch.pl}]
indent_style = tab indent_style = tab
indent_size = 8 indent_size = 8
emacs_mode = perl emacs_mode = perl

View file

@ -45,8 +45,8 @@ variables:
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG' - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG'
when: never when: never
# Scheduled runs on mainline don't get pipelines except for the special Coverity job # Scheduled jobs should explicitly enable the run in their job rules
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_PIPELINE_SOURCE == "schedule"' - if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never when: never
# Cirrus jobs can't run unless the creds / target repo are set # Cirrus jobs can't run unless the creds / target repo are set

View file

@ -83,14 +83,18 @@
.native_test_job_template: .native_test_job_template:
extends: .common_test_job_template extends: .common_test_job_template
before_script:
# Prevent logs from the build job that run earlier
# from being duplicated in the test job artifacts
- rm -f build/meson-logs/*
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always when: always
expire_in: 7 days expire_in: 7 days
paths: paths:
- build/meson-logs/testlog.txt - build/meson-logs
reports: reports:
junit: build/meson-logs/testlog.junit.xml junit: build/meson-logs/*.junit.xml
.functional_test_job_template: .functional_test_job_template:
extends: .common_test_job_template extends: .common_test_job_template
@ -104,14 +108,16 @@
when: always when: always
expire_in: 7 days expire_in: 7 days
paths: paths:
- build/tests/results/latest/results.xml - build/meson-logs
- build/tests/results/latest/test-results
- build/tests/functional/*/*/*.log - build/tests/functional/*/*/*.log
reports: reports:
junit: build/tests/results/latest/results.xml junit: build/meson-logs/*.junit.xml
before_script: before_script:
- export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1
- export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache
# Prevent logs from the build job that run earlier
# from being duplicated in the test job artifacts
- rm -f build/meson-logs/*
after_script: after_script:
- cd build - cd build
- du -chs ${CI_PROJECT_DIR}/*-cache - du -chs ${CI_PROJECT_DIR}/*-cache

View file

@ -36,12 +36,12 @@ build-system-ubuntu:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-ubuntu2204-container - job: amd64-ubuntu2204-container
variables: variables:
IMAGE: ubuntu2204 IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-docs --enable-rust CONFIGURE_ARGS: --enable-docs --enable-rust
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
MAKE_CHECK_ARGS: check-build check-doc MAKE_CHECK_ARGS: check-build
check-system-ubuntu: check-system-ubuntu:
extends: .native_test_job_template extends: .native_test_job_template
@ -66,7 +66,7 @@ build-system-debian:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-debian-container - job: amd64-debian-container
variables: variables:
IMAGE: debian IMAGE: debian
CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust
@ -109,7 +109,7 @@ build-system-fedora:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-fedora-container - job: amd64-fedora-container
variables: variables:
IMAGE: fedora IMAGE: fedora
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
@ -122,7 +122,7 @@ build-system-fedora-rust-nightly:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-fedora-rust-nightly-container - job: amd64-fedora-rust-nightly-container
variables: variables:
IMAGE: fedora-rust-nightly IMAGE: fedora-rust-nightly
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
@ -167,7 +167,7 @@ build-system-centos:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-centos9-container - job: amd64-centos9-container
variables: variables:
IMAGE: centos9 IMAGE: centos9
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server
@ -189,7 +189,7 @@ build-previous-qemu:
- build-previous/tests/qtest/migration-test - build-previous/tests/qtest/migration-test
- build-previous/scripts - build-previous/scripts
needs: needs:
job: amd64-opensuse-leap-container - job: amd64-opensuse-leap-container
variables: variables:
IMAGE: opensuse-leap IMAGE: opensuse-leap
TARGETS: x86_64-softmmu aarch64-softmmu TARGETS: x86_64-softmmu aarch64-softmmu
@ -274,7 +274,7 @@ build-system-opensuse:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-opensuse-leap-container - job: amd64-opensuse-leap-container
variables: variables:
IMAGE: opensuse-leap IMAGE: opensuse-leap
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
@ -308,7 +308,7 @@ build-system-flaky:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-debian-container - job: amd64-debian-container
variables: variables:
IMAGE: debian IMAGE: debian
QEMU_JOB_OPTIONAL: 1 QEMU_JOB_OPTIONAL: 1
@ -338,7 +338,7 @@ functional-system-flaky:
build-tcg-disabled: build-tcg-disabled:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-centos9-container - job: amd64-centos9-container
variables: variables:
IMAGE: centos9 IMAGE: centos9
script: script:
@ -364,7 +364,7 @@ build-tcg-disabled:
build-user: build-user:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-user-cross-container - job: amd64-debian-user-cross-container
variables: variables:
IMAGE: debian-all-test-cross IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools --disable-system CONFIGURE_ARGS: --disable-tools --disable-system
@ -374,7 +374,7 @@ build-user:
build-user-static: build-user-static:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-user-cross-container - job: amd64-debian-user-cross-container
variables: variables:
IMAGE: debian-all-test-cross IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools --disable-system --static CONFIGURE_ARGS: --disable-tools --disable-system --static
@ -385,7 +385,7 @@ build-user-static:
build-legacy: build-legacy:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-legacy-cross-container - job: amd64-debian-legacy-cross-container
variables: variables:
IMAGE: debian-legacy-test-cross IMAGE: debian-legacy-test-cross
TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user
@ -395,7 +395,7 @@ build-legacy:
build-user-hexagon: build-user-hexagon:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: hexagon-cross-container - job: hexagon-cross-container
variables: variables:
IMAGE: debian-hexagon-cross IMAGE: debian-hexagon-cross
TARGETS: hexagon-linux-user TARGETS: hexagon-linux-user
@ -408,7 +408,7 @@ build-user-hexagon:
build-some-softmmu: build-some-softmmu:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-user-cross-container - job: amd64-debian-user-cross-container
variables: variables:
IMAGE: debian-all-test-cross IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools --enable-debug CONFIGURE_ARGS: --disable-tools --enable-debug
@ -419,7 +419,7 @@ build-some-softmmu:
build-loongarch64: build-loongarch64:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: loongarch-debian-cross-container - job: loongarch-debian-cross-container
variables: variables:
IMAGE: debian-loongarch-cross IMAGE: debian-loongarch-cross
CONFIGURE_ARGS: --disable-tools --enable-debug CONFIGURE_ARGS: --disable-tools --enable-debug
@ -430,7 +430,7 @@ build-loongarch64:
build-tricore-softmmu: build-tricore-softmmu:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: tricore-debian-cross-container - job: tricore-debian-cross-container
variables: variables:
IMAGE: debian-tricore-cross IMAGE: debian-tricore-cross
CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug
@ -440,7 +440,7 @@ build-tricore-softmmu:
clang-system: clang-system:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-fedora-container - job: amd64-fedora-container
variables: variables:
IMAGE: fedora IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
@ -451,7 +451,7 @@ clang-system:
clang-user: clang-user:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-user-cross-container - job: amd64-debian-user-cross-container
timeout: 70m timeout: 70m
variables: variables:
IMAGE: debian-all-test-cross IMAGE: debian-all-test-cross
@ -479,7 +479,7 @@ build-cfi-aarch64:
LD_JOBS: 1 LD_JOBS: 1
AR: llvm-ar AR: llvm-ar
IMAGE: fedora IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
--enable-safe-stack --disable-slirp --enable-safe-stack --disable-slirp
TARGETS: aarch64-softmmu TARGETS: aarch64-softmmu
MAKE_CHECK_ARGS: check-build MAKE_CHECK_ARGS: check-build
@ -517,7 +517,7 @@ build-cfi-ppc64-s390x:
LD_JOBS: 1 LD_JOBS: 1
AR: llvm-ar AR: llvm-ar
IMAGE: fedora IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
--enable-safe-stack --disable-slirp --enable-safe-stack --disable-slirp
TARGETS: ppc64-softmmu s390x-softmmu TARGETS: ppc64-softmmu s390x-softmmu
MAKE_CHECK_ARGS: check-build MAKE_CHECK_ARGS: check-build
@ -555,7 +555,7 @@ build-cfi-x86_64:
LD_JOBS: 1 LD_JOBS: 1
AR: llvm-ar AR: llvm-ar
IMAGE: fedora IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
--enable-safe-stack --disable-slirp --enable-safe-stack --disable-slirp
TARGETS: x86_64-softmmu TARGETS: x86_64-softmmu
MAKE_CHECK_ARGS: check-build MAKE_CHECK_ARGS: check-build
@ -582,7 +582,7 @@ functional-cfi-x86_64:
tsan-build: tsan-build:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-ubuntu2204-container - job: amd64-ubuntu2204-container
variables: variables:
IMAGE: ubuntu2204 IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++ CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
@ -596,7 +596,7 @@ tsan-build:
gcov: gcov:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-ubuntu2204-container - job: amd64-ubuntu2204-container
timeout: 80m timeout: 80m
variables: variables:
IMAGE: ubuntu2204 IMAGE: ubuntu2204
@ -613,9 +613,9 @@ gcov:
when: always when: always
expire_in: 2 days expire_in: 2 days
paths: paths:
- build/meson-logs/testlog.txt - build/meson-logs
reports: reports:
junit: build/meson-logs/testlog.junit.xml junit: build/meson-logs/*.junit.xml
coverage_report: coverage_report:
coverage_format: cobertura coverage_format: cobertura
path: build/coverage.xml path: build/coverage.xml
@ -623,7 +623,7 @@ gcov:
build-oss-fuzz: build-oss-fuzz:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-fedora-container - job: amd64-fedora-container
variables: variables:
IMAGE: fedora IMAGE: fedora
script: script:
@ -645,7 +645,7 @@ build-oss-fuzz:
build-tci: build-tci:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-user-cross-container - job: amd64-debian-user-cross-container
variables: variables:
IMAGE: debian-all-test-cross IMAGE: debian-all-test-cross
script: script:
@ -656,21 +656,19 @@ build-tci:
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|| { cat config.log meson-logs/meson-log.txt && exit 1; } || { cat config.log meson-logs/meson-log.txt && exit 1; }
- make -j"$JOBS" - make -j"$JOBS"
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test - make tests/qtest/boot-serial-test tests/qtest/cdrom-test
- for tg in $TARGETS ; do - for tg in $TARGETS ; do
export QTEST_QEMU_BINARY="./qemu-system-${tg}" ; export QTEST_QEMU_BINARY="./qemu-system-${tg}" ;
./tests/qtest/boot-serial-test || exit 1 ; ./tests/qtest/boot-serial-test || exit 1 ;
./tests/qtest/cdrom-test || exit 1 ; ./tests/qtest/cdrom-test || exit 1 ;
done done
- QTEST_QEMU_BINARY="./qemu-system-x86_64" ./tests/qtest/pxe-test
- QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow
- make check-tcg - make check-tcg
# Check our reduced build configurations # Check our reduced build configurations
build-without-defaults: build-without-defaults:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-centos9-container - job: amd64-centos9-container
variables: variables:
IMAGE: centos9 IMAGE: centos9
CONFIGURE_ARGS: CONFIGURE_ARGS:
@ -688,7 +686,7 @@ build-libvhost-user:
stage: build stage: build
image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG
needs: needs:
job: amd64-fedora-container - job: amd64-fedora-container
script: script:
- mkdir subprojects/libvhost-user/build - mkdir subprojects/libvhost-user/build
- cd subprojects/libvhost-user/build - cd subprojects/libvhost-user/build
@ -702,7 +700,7 @@ build-tools-and-docs-debian:
- .native_build_job_template - .native_build_job_template
- .native_build_artifact_template - .native_build_artifact_template
needs: needs:
job: amd64-debian-container - job: amd64-debian-container
# when running on 'master' we use pre-existing container # when running on 'master' we use pre-existing container
optional: true optional: true
variables: variables:
@ -736,7 +734,7 @@ pages:
- make gtags - make gtags
# We unset variables to work around a bug in some htags versions # We unset variables to work around a bug in some htags versions
# which causes it to fail when the environment is large # which causes it to fail when the environment is large
- CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= CI_COMMIT_DESCRIPTION= htags
-anT --tree-view=filetree -m qemu_init -anT --tree-view=filetree -m qemu_init
-t "Welcome to the QEMU sourcecode" -t "Welcome to the QEMU sourcecode"
- mv HTML public/src - mv HTML public/src
@ -759,7 +757,7 @@ coverity:
- job: amd64-fedora-container - job: amd64-fedora-container
optional: true optional: true
before_script: before_script:
- dnf install -y curl wget - dnf install -y curl wget file
script: script:
# would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089) # would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089)
# for example: # for example:
@ -791,7 +789,7 @@ build-wasm:
extends: .wasm_build_job_template extends: .wasm_build_job_template
timeout: 2h timeout: 2h
needs: needs:
job: wasm-emsdk-cross-container - job: wasm-emsdk-cross-container
variables: variables:
IMAGE: emsdk-wasm32-cross IMAGE: emsdk-wasm32-cross
CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter

View file

@ -37,12 +37,12 @@ x64-freebsd-14-build:
NAME: freebsd-14 NAME: freebsd-14
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
CIRRUS_VM_IMAGE_SELECTOR: image_family CIRRUS_VM_IMAGE_SELECTOR: image_family
CIRRUS_VM_IMAGE_NAME: freebsd-14-2 CIRRUS_VM_IMAGE_NAME: freebsd-14-3
CIRRUS_VM_CPUS: 8 CIRRUS_VM_CPUS: 8
CIRRUS_VM_RAM: 8G CIRRUS_VM_RAM: 8G
UPDATE_COMMAND: pkg update; pkg upgrade -y UPDATE_COMMAND: pkg update; pkg upgrade -y
INSTALL_COMMAND: pkg install -y INSTALL_COMMAND: pkg install -y
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu --enable-rust
TEST_TARGETS: check TEST_TARGETS: check
aarch64-macos-build: aarch64-macos-build:

View file

@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja' NINJA='/usr/local/bin/ninja'
PACKAGING_COMMAND='pkg' PACKAGING_COMMAND='pkg'
PIP3='/usr/local/bin/pip' PIP3='/usr/local/bin/pip'
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 vulkan-tools xorriso zstd' PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka coreutils ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 vulkan-tools xorriso zstd'
PYPI_PKGS='' PYPI_PKGS=''
PYTHON='/usr/local/bin/python3' PYTHON='/usr/local/bin/python3'

View file

@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake'
NINJA='/opt/homebrew/bin/ninja' NINJA='/opt/homebrew/bin/ninja'
PACKAGING_COMMAND='brew' PACKAGING_COMMAND='brew'
PIP3='/opt/homebrew/bin/pip3' PIP3='/opt/homebrew/bin/pip3'
PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd' PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka coreutils ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd'
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
PYTHON='/opt/homebrew/bin/python3' PYTHON='/opt/homebrew/bin/python3'

View file

@ -52,12 +52,6 @@ mips64el-debian-cross-container:
variables: variables:
NAME: debian-mips64el-cross NAME: debian-mips64el-cross
mipsel-debian-cross-container:
extends: .container_job_template
stage: containers
variables:
NAME: debian-mipsel-cross
ppc64el-debian-cross-container: ppc64el-debian-cross-container:
extends: .container_job_template extends: .container_job_template
stage: containers stage: containers

View file

@ -19,3 +19,7 @@
- docker push "$TAG" - docker push "$TAG"
after_script: after_script:
- docker logout - docker logout
rules:
# because we want to enable this for scheduled runs we also have to replicate the normal rules
- if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM'
- !reference [.base_job_template, rules]

View file

@ -33,3 +33,42 @@ amd64-fedora-rust-nightly-container:
variables: variables:
NAME: fedora-rust-nightly NAME: fedora-rust-nightly
allow_failure: true allow_failure: true
# this scheduled job will trigger all the containers to build
weekly-container-builds:
extends: .container_job_template
allow_failure: true
needs:
# core
- amd64-centos9-container
- amd64-fedora-container
# cross
- amd64-debian-cross-container
- amd64-debian-user-cross-container
- amd64-debian-legacy-cross-container
- arm64-debian-cross-container
- armhf-debian-cross-container
- hexagon-cross-container
- loongarch-debian-cross-container
- i686-debian-cross-container
- mips64el-debian-cross-container
- ppc64el-debian-cross-container
- riscv64-debian-cross-container
- s390x-debian-cross-container
- tricore-debian-cross-container
- xtensa-debian-cross-container
- win64-fedora-cross-container
- wasm-emsdk-cross-container
# containers
- amd64-alpine-container
- amd64-debian-container
- amd64-ubuntu2204-container
- amd64-opensuse-leap-container
- python-container
- amd64-fedora-rust-nightly-container
script:
- apk -U add make bash skopeo
- make docker-verify V=1 DOCKER_DEFAULT_REGISTRY=$CI_REGISTRY_IMAGE
rules:
# this only ever runes as a scheduled build
- if: '$CI_PIPELINE_SOURCE == "schedule"'

View file

@ -128,6 +128,6 @@
when: always when: always
expire_in: 7 days expire_in: 7 days
paths: paths:
- build/meson-logs/testlog.txt - build/meson-logs
reports: reports:
junit: build/meson-logs/testlog.junit.xml junit: build/meson-logs/*.junit.xml

View file

@ -4,28 +4,28 @@ include:
cross-armhf-user: cross-armhf-user:
extends: .cross_user_build_job extends: .cross_user_build_job
needs: needs:
job: armhf-debian-cross-container - job: armhf-debian-cross-container
variables: variables:
IMAGE: debian-armhf-cross IMAGE: debian-armhf-cross
cross-arm64-system: cross-arm64-system:
extends: .cross_system_build_job extends: .cross_system_build_job
needs: needs:
job: arm64-debian-cross-container - job: arm64-debian-cross-container
variables: variables:
IMAGE: debian-arm64-cross IMAGE: debian-arm64-cross
cross-arm64-user: cross-arm64-user:
extends: .cross_user_build_job extends: .cross_user_build_job
needs: needs:
job: arm64-debian-cross-container - job: arm64-debian-cross-container
variables: variables:
IMAGE: debian-arm64-cross IMAGE: debian-arm64-cross
cross-arm64-kvm-only: cross-arm64-kvm-only:
extends: .cross_accel_build_job extends: .cross_accel_build_job
needs: needs:
job: arm64-debian-cross-container - job: arm64-debian-cross-container
variables: variables:
IMAGE: debian-arm64-cross IMAGE: debian-arm64-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features
@ -35,7 +35,7 @@ cross-i686-system:
- .cross_system_build_job - .cross_system_build_job
- .cross_test_artifacts - .cross_test_artifacts
needs: needs:
job: i686-debian-cross-container - job: i686-debian-cross-container
variables: variables:
IMAGE: debian-i686-cross IMAGE: debian-i686-cross
EXTRA_CONFIGURE_OPTS: --disable-kvm EXTRA_CONFIGURE_OPTS: --disable-kvm
@ -46,7 +46,7 @@ cross-i686-user:
- .cross_user_build_job - .cross_user_build_job
- .cross_test_artifacts - .cross_test_artifacts
needs: needs:
job: i686-debian-cross-container - job: i686-debian-cross-container
variables: variables:
IMAGE: debian-i686-cross IMAGE: debian-i686-cross
MAKE_CHECK_ARGS: check MAKE_CHECK_ARGS: check
@ -57,7 +57,7 @@ cross-i686-tci:
- .cross_test_artifacts - .cross_test_artifacts
timeout: 60m timeout: 60m
needs: needs:
job: i686-debian-cross-container - job: i686-debian-cross-container
variables: variables:
IMAGE: debian-i686-cross IMAGE: debian-i686-cross
ACCEL: tcg-interpreter ACCEL: tcg-interpreter
@ -68,52 +68,38 @@ cross-i686-tci:
# would otherwise be using a parallelism of 9. # would otherwise be using a parallelism of 9.
MAKE_CHECK_ARGS: check check-tcg -j2 MAKE_CHECK_ARGS: check check-tcg -j2
cross-mipsel-system:
extends: .cross_system_build_job
needs:
job: mipsel-debian-cross-container
variables:
IMAGE: debian-mipsel-cross
cross-mipsel-user:
extends: .cross_user_build_job
needs:
job: mipsel-debian-cross-container
variables:
IMAGE: debian-mipsel-cross
cross-mips64el-system: cross-mips64el-system:
extends: .cross_system_build_job extends: .cross_system_build_job
needs: needs:
job: mips64el-debian-cross-container - job: mips64el-debian-cross-container
variables: variables:
IMAGE: debian-mips64el-cross IMAGE: debian-mips64el-cross
cross-mips64el-user: cross-mips64el-user:
extends: .cross_user_build_job extends: .cross_user_build_job
needs: needs:
job: mips64el-debian-cross-container - job: mips64el-debian-cross-container
variables: variables:
IMAGE: debian-mips64el-cross IMAGE: debian-mips64el-cross
cross-ppc64el-system: cross-ppc64el-system:
extends: .cross_system_build_job extends: .cross_system_build_job
needs: needs:
job: ppc64el-debian-cross-container - job: ppc64el-debian-cross-container
variables: variables:
IMAGE: debian-ppc64el-cross IMAGE: debian-ppc64el-cross
cross-ppc64el-user: cross-ppc64el-user:
extends: .cross_user_build_job extends: .cross_user_build_job
needs: needs:
job: ppc64el-debian-cross-container - job: ppc64el-debian-cross-container
variables: variables:
IMAGE: debian-ppc64el-cross IMAGE: debian-ppc64el-cross
cross-ppc64el-kvm-only: cross-ppc64el-kvm-only:
extends: .cross_accel_build_job extends: .cross_accel_build_job
needs: needs:
job: ppc64el-debian-cross-container - job: ppc64el-debian-cross-container
variables: variables:
IMAGE: debian-ppc64el-cross IMAGE: debian-ppc64el-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
@ -121,35 +107,35 @@ cross-ppc64el-kvm-only:
cross-riscv64-system: cross-riscv64-system:
extends: .cross_system_build_job extends: .cross_system_build_job
needs: needs:
job: riscv64-debian-cross-container - job: riscv64-debian-cross-container
variables: variables:
IMAGE: debian-riscv64-cross IMAGE: debian-riscv64-cross
cross-riscv64-user: cross-riscv64-user:
extends: .cross_user_build_job extends: .cross_user_build_job
needs: needs:
job: riscv64-debian-cross-container - job: riscv64-debian-cross-container
variables: variables:
IMAGE: debian-riscv64-cross IMAGE: debian-riscv64-cross
cross-s390x-system: cross-s390x-system:
extends: .cross_system_build_job extends: .cross_system_build_job
needs: needs:
job: s390x-debian-cross-container - job: s390x-debian-cross-container
variables: variables:
IMAGE: debian-s390x-cross IMAGE: debian-s390x-cross
cross-s390x-user: cross-s390x-user:
extends: .cross_user_build_job extends: .cross_user_build_job
needs: needs:
job: s390x-debian-cross-container - job: s390x-debian-cross-container
variables: variables:
IMAGE: debian-s390x-cross IMAGE: debian-s390x-cross
cross-s390x-kvm-only: cross-s390x-kvm-only:
extends: .cross_accel_build_job extends: .cross_accel_build_job
needs: needs:
job: s390x-debian-cross-container - job: s390x-debian-cross-container
variables: variables:
IMAGE: debian-s390x-cross IMAGE: debian-s390x-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace
@ -157,7 +143,7 @@ cross-s390x-kvm-only:
cross-mips64el-kvm-only: cross-mips64el-kvm-only:
extends: .cross_accel_build_job extends: .cross_accel_build_job
needs: needs:
job: mips64el-debian-cross-container - job: mips64el-debian-cross-container
variables: variables:
IMAGE: debian-mips64el-cross IMAGE: debian-mips64el-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu
@ -165,7 +151,7 @@ cross-mips64el-kvm-only:
cross-win64-system: cross-win64-system:
extends: .cross_system_build_job extends: .cross_system_build_job
needs: needs:
job: win64-fedora-cross-container - job: win64-fedora-cross-container
variables: variables:
IMAGE: fedora-win64-cross IMAGE: fedora-win64-cross
EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins
@ -181,7 +167,7 @@ cross-win64-system:
cross-amd64-xen-only: cross-amd64-xen-only:
extends: .cross_accel_build_job extends: .cross_accel_build_job
needs: needs:
job: amd64-debian-cross-container - job: amd64-debian-cross-container
variables: variables:
IMAGE: debian-amd64-cross IMAGE: debian-amd64-cross
ACCEL: xen ACCEL: xen
@ -190,7 +176,7 @@ cross-amd64-xen-only:
cross-arm64-xen-only: cross-arm64-xen-only:
extends: .cross_accel_build_job extends: .cross_accel_build_job
needs: needs:
job: arm64-debian-cross-container - job: arm64-debian-cross-container
variables: variables:
IMAGE: debian-arm64-cross IMAGE: debian-arm64-cross
ACCEL: xen ACCEL: xen

View file

@ -26,9 +26,9 @@
- build/build.ninja - build/build.ninja
- build/meson-logs - build/meson-logs
reports: reports:
junit: build/meson-logs/testlog.junit.xml junit: build/meson-logs/*.junit.xml
include: include:
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml' - local: '/.gitlab-ci.d/custom-runners/debian-13-ppc64le.yml'

View file

@ -0,0 +1,45 @@
# All jobs should run successfully in an environment setup by the
# scripts/ci/setup/build-environment.yml task:
# "Install basic packages to build QEMU on Ubuntu/Debian"
.debian_ppc64le_template:
extends: .custom_runner_template
needs: []
stage: build
tags:
- debian_13
- ppc64le
rules:
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /^staging/'
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: never
- if: '$PPC64LE_RUNNER_AVAILABLE && $CI_PIPELINE_SOURCE != "schedule" && $QEMU_CI == "1"'
when: manual
- if: '$PPC64LE_RUNNER_AVAILABLE && $CI_PIPELINE_SOURCE != "schedule"'
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Pre-script setup"
- JOBS=$(expr $(nproc) - 2)
- section_end setup
script:
- mkdir build
- cd build
- section_start configure "Running configure"
- ../configure $CONFIGURE_ARGS ||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
- section_end configure
- section_start build "Building QEMU"
- make --output-sync -j"$JOBS"
- section_end build
- section_start test "Running tests"
- if test -n "$MAKE_CHECK_ARGS";
then
make -j"$JOBS" $MAKE_CHECK_ARGS ;
fi
- section_end test
debian-13-ppc64le-default:
extends: .debian_ppc64le_template
variables:
# qtest currently fails: https://gitlab.com/qemu-project/qemu/-/issues/3207
MAKE_CHECK_ARGS: check-unit check-tcg check-softfloat

View file

@ -1,25 +0,0 @@
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 22.04"
ubuntu-22.04-aarch32-all:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch32
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH32_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --cross-prefix=arm-linux-gnueabihf-
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check

View file

@ -1,151 +0,0 @@
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 22.04"
ubuntu-22.04-aarch64-all-linux-static:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$AARCH64_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
# Disable -static-pie due to build error with system libc:
# https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1987438
- ../configure --enable-debug --static --disable-system --disable-pie
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make check-tcg
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-all:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-without-defaults:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-user --without-default-devices --without-default-features
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-alldbg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$AARCH64_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --enable-debug
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make clean
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-clang:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-ubsan
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-tci:
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --enable-tcg-interpreter
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
ubuntu-22.04-aarch64-notcg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-tcg --with-devices-aarch64=minimal
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check

View file

@ -1,130 +0,0 @@
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 22.04"
ubuntu-22.04-s390x-all-linux:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --enable-debug --disable-system --disable-tools --disable-docs
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync check-tcg
- make --output-sync -j`nproc` check
ubuntu-22.04-s390x-all-system:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- s390x
timeout: 75m
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
allow_failure: true
- if: "$S390X_RUNNER_AVAILABLE"
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-user
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
ubuntu-22.04-s390x-alldbg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --enable-debug
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make clean
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
ubuntu-22.04-s390x-clang:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --cc=clang --cxx=clang++ --enable-ubsan
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
ubuntu-22.04-s390x-tci:
needs: []
stage: build
tags:
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --enable-tcg-interpreter
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
ubuntu-22.04-s390x-notcg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-tcg
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check

View file

@ -0,0 +1,90 @@
# All ubuntu-24.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 24.04"
.ubuntu_aarch64_template:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: never
- if: '$AARCH64_RUNNER_AVAILABLE && $CI_PIPELINE_SOURCE != "schedule" && $QEMU_CI == "1"'
when: manual
- if: '$AARCH64_RUNNER_AVAILABLE && $CI_PIPELINE_SOURCE != "schedule"'
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Pre-script setup"
- JOBS=$(expr $(nproc) - 4)
- section_end setup
script:
- mkdir build
- cd build
- section_start configure "Running configure"
- ../configure $CONFIGURE_ARGS ||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
- section_end configure
- section_start build "Building QEMU"
- make --output-sync -j"$JOBS"
- section_end build
- section_start test "Running tests"
- if test -n "$MAKE_CHECK_ARGS";
then
make -j"$JOBS" $MAKE_CHECK_ARGS ;
fi
- section_end test
ubuntu-24.04-aarch64-all-linux-static:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --enable-debug --static --disable-system
MAKE_CHECK_ARGS: check-tcg
ubuntu-24.04-aarch64-all:
extends: .ubuntu_aarch64_template
variables:
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual
ubuntu-24.04-aarch64-without-defaults:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --disable-user --without-default-devices --without-default-features
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual
ubuntu-24.04-aarch64-alldbg:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --enable-debug
MAKE_CHECK_ARGS: check-tcg
ubuntu-24.04-aarch64-clang:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual
ubuntu-24.04-aarch64-tci:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --enable-tcg-interpreter
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual
ubuntu-24.04-aarch64-notcg:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --disable-tcg --with-devices-aarch64=minimal
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual

View file

@ -0,0 +1,89 @@
# All ubuntu-24.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 24.04"
.ubuntu_s390x_template:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /^staging/'
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: never
- if: '$S390X_RUNNER_AVAILABLE && $CI_PIPELINE_SOURCE != "schedule" && $QEMU_CI == "1"'
when: manual
- if: '$S390X_RUNNER_AVAILABLE && $CI_PIPELINE_SOURCE != "schedule"'
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Pre-script setup"
- JOBS=$(expr $(nproc) - 1)
- section_end setup
script:
- mkdir build
- cd build
- section_start configure "Running configure"
- ../configure $CONFIGURE_ARGS ||
{ cat config.log meson-logs/meson-log.txt; exit 1; }
- section_end configure
- section_start build "Building QEMU"
- make --output-sync -j"$JOBS"
- section_end build
- section_start test "Running tests"
- if test -n "$MAKE_CHECK_ARGS";
then
make -j"$JOBS" $MAKE_CHECK_ARGS ;
fi
- section_end test
ubuntu-24.04-s390x-all-linux:
extends: .ubuntu_s390x_template
needs: []
variables:
CONFIGURE_ARGS: --enable-debug --disable-system --disable-tools --disable-docs
MAKE_CHECK_ARGS: check-tcg check
ubuntu-24.04-s390x-all-system:
extends: .ubuntu_s390x_template
needs: []
variables:
CONFIGURE_ARGS: --disable-user
MAKE_CHECK_ARGS: check
allow_failure: true
ubuntu-24.04-s390x-alldbg:
extends: .ubuntu_s390x_template
needs: []
variables:
CONFIGURE_ARGS: --enable-debug
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual
ubuntu-24.04-s390x-clang:
extends: .ubuntu_s390x_template
needs: []
variables:
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual
ubuntu-24.04-s390x-tci:
extends: .ubuntu_s390x_template
needs: []
variables:
CONFIGURE_ARGS: --enable-tcg-interpreter
allow_failure: true
when: manual
ubuntu-24.04-s390x-notcg:
extends: .ubuntu_s390x_template
needs: []
variables:
CONFIGURE_ARGS: --disable-tcg
MAKE_CHECK_ARGS: check
allow_failure: true
when: manual

View file

@ -32,7 +32,7 @@ check-python-minreqs:
variables: variables:
GIT_DEPTH: 1 GIT_DEPTH: 1
needs: needs:
job: python-container - job: python-container
check-python-tox: check-python-tox:
extends: .base_job_template extends: .base_job_template
@ -45,7 +45,7 @@ check-python-tox:
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
QEMU_JOB_OPTIONAL: 1 QEMU_JOB_OPTIONAL: 1
needs: needs:
job: python-container - job: python-container
check-rust-tools-nightly: check-rust-tools-nightly:
extends: .base_job_template extends: .base_job_template
@ -76,7 +76,7 @@ check-build-units:
stage: build stage: build
image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG
needs: needs:
job: amd64-debian-container - job: amd64-debian-container
before_script: before_script:
- source scripts/ci/gitlab-ci-section - source scripts/ci/gitlab-ci-section
- section_start setup "Install Tools" - section_start setup "Install Tools"

View file

@ -24,9 +24,10 @@ msys2-64bit:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
expire_in: 7 days expire_in: 7 days
paths: paths:
- build/meson-logs/testlog.txt - build/meson-logs
- build/cache-log.txt
reports: reports:
junit: "build/meson-logs/testlog.junit.xml" junit: build/meson-logs/*.junit.xml
before_script: before_script:
- Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)" - Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)"
- If ( !(Test-Path -Path msys64\var\cache ) ) { - If ( !(Test-Path -Path msys64\var\cache ) ) {
@ -77,7 +78,7 @@ msys2-64bit:
git grep make sed git grep make sed
mingw-w64-x86_64-binutils mingw-w64-x86_64-binutils
mingw-w64-x86_64-ccache mingw-w64-x86_64-ccache
mingw-w64-x86_64-curl mingw-w64-x86_64-curl-winssl
mingw-w64-x86_64-gcc mingw-w64-x86_64-gcc
mingw-w64-x86_64-glib2 mingw-w64-x86_64-glib2
mingw-w64-x86_64-libnfs mingw-w64-x86_64-libnfs
@ -87,13 +88,14 @@ msys2-64bit:
mingw-w64-x86_64-pkgconf mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-python mingw-w64-x86_64-python
mingw-w64-x86_64-zstd" mingw-w64-x86_64-zstd"
- .\msys64\usr\bin\bash -lc "pacman -Sc --noconfirm"
- Write-Output "Running build at $(Get-Date -Format u)" - Write-Output "Running build at $(Get-Date -Format u)"
- $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc) - $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc)
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR" - $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR"
- $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache" - $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache"
- $env:CCACHE_MAXSIZE = "500M" - $env:CCACHE_MAXSIZE = "180M"
- $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode - $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode
- $env:CC = "ccache gcc" - $env:CC = "ccache gcc"
- mkdir build - mkdir build
@ -102,5 +104,7 @@ msys2-64bit:
- ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS" - ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS"
- ..\msys64\usr\bin\bash -lc "make -j$env:JOBS" - ..\msys64\usr\bin\bash -lc "make -j$env:JOBS"
- ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;" - ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;"
- ..\msys64\usr\bin\bash -lc "ls -lR /var/cache > cache-log.txt"
- ..\msys64\usr\bin\bash -lc "du -sh ."
- ..\msys64\usr\bin\bash -lc "ccache --show-stats" - ..\msys64\usr\bin\bash -lc "ccache --show-stats"
- Write-Output "Finished build at $(Get-Date -Format u)" - Write-Output "Finished build at $(Get-Date -Format u)"

2
.gitmodules vendored
View file

@ -15,6 +15,7 @@
url = https://gitlab.com/qemu-project/qemu-palcode.git url = https://gitlab.com/qemu-project/qemu-palcode.git
[submodule "roms/u-boot"] [submodule "roms/u-boot"]
path = roms/u-boot path = roms/u-boot
# upstream is https://github.com/u-boot/u-boot
url = https://gitlab.com/qemu-project/u-boot.git url = https://gitlab.com/qemu-project/u-boot.git
[submodule "roms/skiboot"] [submodule "roms/skiboot"]
path = roms/skiboot path = roms/skiboot
@ -27,6 +28,7 @@
url = https://gitlab.com/qemu-project/seabios-hppa.git url = https://gitlab.com/qemu-project/seabios-hppa.git
[submodule "roms/u-boot-sam460ex"] [submodule "roms/u-boot-sam460ex"]
path = roms/u-boot-sam460ex path = roms/u-boot-sam460ex
# upstream is https://github.com/zbalaton/u-boot-sam460ex
url = https://gitlab.com/qemu-project/u-boot-sam460ex.git url = https://gitlab.com/qemu-project/u-boot-sam460ex.git
[submodule "roms/edk2"] [submodule "roms/edk2"]
path = roms/edk2 path = roms/edk2

View file

@ -4,48 +4,48 @@
# See https://github.com/stefanha/git-publish for more information # See https://github.com/stefanha/git-publish for more information
# #
[gitpublishprofile "default"] [gitpublishprofile "default"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "rfc"] [gitpublishprofile "rfc"]
base = master base = origin/master
prefix = RFC PATCH prefix = RFC PATCH
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "stable"] [gitpublishprofile "stable"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cc = qemu-stable@nongnu.org cc = qemu-stable@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "trivial"] [gitpublishprofile "trivial"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cc = qemu-trivial@nongnu.org cc = qemu-trivial@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "block"] [gitpublishprofile "block"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cc = qemu-block@nongnu.org cc = qemu-block@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "arm"] [gitpublishprofile "arm"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cc = qemu-arm@nongnu.org cc = qemu-arm@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "s390"] [gitpublishprofile "s390"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cc = qemu-s390@nongnu.org cc = qemu-s390@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "ppc"] [gitpublishprofile "ppc"]
base = master base = origin/master
to = qemu-devel@nongnu.org to = qemu-devel@nongnu.org
cc = qemu-ppc@nongnu.org cc = qemu-ppc@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null

View file

@ -74,6 +74,7 @@ Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgte
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com> Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com> Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com> Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
Alex Williamson <alex@shazbot.org> <alex.williamson@redhat.com>
Alexander Graf <agraf@csgraf.de> <agraf@suse.de> Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca> Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
@ -81,6 +82,7 @@ Brian Cain <brian.cain@oss.qualcomm.com> <bcain@quicinc.com>
Brian Cain <brian.cain@oss.qualcomm.com> <quic_bcain@quicinc.com> Brian Cain <brian.cain@oss.qualcomm.com> <quic_bcain@quicinc.com>
Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.com> Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.com>
Damien Hedde <damien.hedde@dahe.fr> <damien.hedde@greensocs.com> Damien Hedde <damien.hedde@dahe.fr> <damien.hedde@greensocs.com>
David Hildenbrand <david@kernel.org> <david@redhat.com>
Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com> Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com>
Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.com> Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.com>
Frederic Konrad <konrad.frederic@yahoo.fr> <konrad@adacore.com> Frederic Konrad <konrad.frederic@yahoo.fr> <konrad@adacore.com>
@ -136,6 +138,7 @@ Chen Gang <gang.chen.5i5j@gmail.com>
Chen Gang <gang.chen@sunrus.com.cn> Chen Gang <gang.chen@sunrus.com.cn>
Chen Wei-Ren <chenwj@iis.sinica.edu.tw> Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
Christophe Lyon <christophe.lyon@st.com> Christophe Lyon <christophe.lyon@st.com>
Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
Collin L. Walling <walling@linux.ibm.com> Collin L. Walling <walling@linux.ibm.com>
Daniel P. Berrangé <berrange@redhat.com> Daniel P. Berrangé <berrange@redhat.com>
Eduardo Otubo <otubo@redhat.com> Eduardo Otubo <otubo@redhat.com>

File diff suppressed because it is too large Load diff

View file

@ -96,6 +96,8 @@ meson.stamp: config-host.mak
# 3. ensure meson-generated build files are up-to-date # 3. ensure meson-generated build files are up-to-date
ninja-cmd-goals =
ifneq ($(NINJA),) ifneq ($(NINJA),)
Makefile.ninja: build.ninja Makefile.ninja: build.ninja
$(quiet-@){ \ $(quiet-@){ \
@ -150,7 +152,7 @@ NINJAFLAGS = \
$(or $(filter -l% -j%, $(MAKEFLAGS)), \ $(or $(filter -l% -j%, $(MAKEFLAGS)), \
$(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \ $(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \
-d keepdepfile -d keepdepfile
ninja-cmd-goals = $(or $(MAKECMDGOALS), all) ninja-cmd-goals += $(or $(MAKECMDGOALS), all)
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g)) ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
makefile-targets := build.ninja ctags TAGS cscope dist clean makefile-targets := build.ninja ctags TAGS cscope dist clean

View file

@ -1 +1 @@
10.1.0 10.2.1

View file

@ -13,6 +13,9 @@ config TCG
config KVM config KVM
bool bool
config MSHV
bool
config XEN config XEN
bool bool
select FSDEV_9P if VIRTFS select FSDEV_9P if VIRTFS

106
accel/accel-irq.c Normal file
View file

@ -0,0 +1,106 @@
/*
* Accelerated irqchip abstraction
*
* Copyright Microsoft, Corp. 2025
*
* Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/pci/msi.h"
#include "system/kvm.h"
#include "system/mshv.h"
#include "system/accel-irq.h"
int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_add_msi_route(vector, dev);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_add_msi_route(c, vector, dev);
}
return -ENOSYS;
}
int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_update_msi_route(vector, msg, dev);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev);
}
return -ENOSYS;
}
void accel_irqchip_commit_route_changes(KVMRouteChange *c)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_commit_routes();
}
#endif
if (kvm_enabled()) {
kvm_irqchip_commit_route_changes(c);
}
}
void accel_irqchip_commit_routes(void)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_commit_routes();
}
#endif
if (kvm_enabled()) {
kvm_irqchip_commit_routes(kvm_state);
}
}
void accel_irqchip_release_virq(int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_release_virq(virq);
}
#endif
if (kvm_enabled()) {
kvm_irqchip_release_virq(kvm_state, virq);
}
}
int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq);
}
return -ENOSYS;
}
int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq);
}
return -ENOSYS;
}

View file

@ -43,6 +43,7 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed); qemu_guest_random_seed_thread_part2(cpu->random_seed);
do { do {
qemu_process_cpu_events(cpu);
bql_unlock(); bql_unlock();
#ifndef _WIN32 #ifndef _WIN32
do { do {
@ -57,7 +58,6 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_sem_wait(&cpu->sem); qemu_sem_wait(&cpu->sem);
#endif #endif
bql_lock(); bql_lock();
qemu_wait_io_event(cpu);
} while (!cpu->unplug); } while (!cpu->unplug);
bql_unlock(); bql_unlock();

View file

@ -81,7 +81,7 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
{ {
if (!cpu->vcpu_dirty) { if (!cpu->vcpu_dirty) {
hvf_get_registers(cpu); hvf_arch_get_registers(cpu);
cpu->vcpu_dirty = true; cpu->vcpu_dirty = true;
} }
} }
@ -148,10 +148,11 @@ static int hvf_init_vcpu(CPUState *cpu)
sigact.sa_handler = dummy_signal; sigact.sa_handler = dummy_signal;
sigaction(SIG_IPI, &sigact, NULL); sigaction(SIG_IPI, &sigact, NULL);
#ifdef __aarch64__
pthread_sigmask(SIG_BLOCK, NULL, &cpu->accel->unblock_ipi_mask); pthread_sigmask(SIG_BLOCK, NULL, &cpu->accel->unblock_ipi_mask);
sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI); sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI);
cpu->accel->guest_debug_enabled = false;
#ifdef __aarch64__
r = hv_vcpu_create(&cpu->accel->fd, r = hv_vcpu_create(&cpu->accel->fd,
(hv_vcpu_exit_t **)&cpu->accel->exit, NULL); (hv_vcpu_exit_t **)&cpu->accel->exit, NULL);
#else #else
@ -160,8 +161,6 @@ static int hvf_init_vcpu(CPUState *cpu)
assert_hvf_ok(r); assert_hvf_ok(r);
cpu->vcpu_dirty = true; cpu->vcpu_dirty = true;
cpu->accel->guest_debug_enabled = false;
return hvf_arch_init_vcpu(cpu); return hvf_arch_init_vcpu(cpu);
} }
@ -192,13 +191,13 @@ static void *hvf_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed); qemu_guest_random_seed_thread_part2(cpu->random_seed);
do { do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) { if (cpu_can_run(cpu)) {
r = hvf_vcpu_exec(cpu); r = hvf_arch_vcpu_exec(cpu);
if (r == EXCP_DEBUG) { if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu); cpu_handle_guest_debug(cpu);
} }
} }
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu)); } while (!cpu->unplug || cpu_can_run(cpu));
hvf_vcpu_destroy(cpu); hvf_vcpu_destroy(cpu);

View file

@ -47,13 +47,14 @@ static void *kvm_vcpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed); qemu_guest_random_seed_thread_part2(cpu->random_seed);
do { do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) { if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu); r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) { if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu); cpu_handle_guest_debug(cpu);
} }
} }
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu)); } while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu); kvm_destroy_vcpu(cpu);

View file

@ -32,11 +32,13 @@
#include "system/runstate.h" #include "system/runstate.h"
#include "system/cpus.h" #include "system/cpus.h"
#include "system/accel-blocker.h" #include "system/accel-blocker.h"
#include "system/physmem.h"
#include "system/ramblock.h"
#include "accel/accel-ops.h" #include "accel/accel-ops.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "exec/tswap.h" #include "exec/tswap.h"
#include "exec/target_page.h"
#include "system/memory.h" #include "system/memory.h"
#include "system/ram_addr.h"
#include "qemu/event_notifier.h" #include "qemu/event_notifier.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "trace.h" #include "trace.h"
@ -358,7 +360,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
{ {
KVMState *s = kvm_state; KVMState *s = kvm_state;
struct kvm_userspace_memory_region2 mem; struct kvm_userspace_memory_region2 mem = {};
int ret; int ret;
mem.slot = slot->slot | (kml->as_id << 16); mem.slot = slot->slot | (kml->as_id << 16);
@ -414,7 +416,7 @@ err:
return ret; return ret;
} }
void kvm_park_vcpu(CPUState *cpu) static void kvm_park_vcpu(CPUState *cpu)
{ {
struct KVMParkedVcpu *vcpu; struct KVMParkedVcpu *vcpu;
@ -426,7 +428,7 @@ void kvm_park_vcpu(CPUState *cpu)
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
} }
int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) static int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
{ {
struct KVMParkedVcpu *cpu; struct KVMParkedVcpu *cpu;
int kvm_fd = -ENOENT; int kvm_fd = -ENOENT;
@ -523,7 +525,8 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
} }
/* If I am the CPU that created coalesced_mmio_ring, then discard it */ /* If I am the CPU that created coalesced_mmio_ring, then discard it */
if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) { if (s->coalesced_mmio_ring ==
(void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE) {
s->coalesced_mmio_ring = NULL; s->coalesced_mmio_ring = NULL;
} }
@ -756,7 +759,7 @@ static void kvm_slot_sync_dirty_pages(KVMSlot *slot)
ram_addr_t start = slot->ram_start_offset; ram_addr_t start = slot->ram_start_offset;
ram_addr_t pages = slot->memory_size / qemu_real_host_page_size(); ram_addr_t pages = slot->memory_size / qemu_real_host_page_size();
cpu_physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages); physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages);
} }
static void kvm_slot_reset_dirty_pages(KVMSlot *slot) static void kvm_slot_reset_dirty_pages(KVMSlot *slot)
@ -1595,7 +1598,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram; mem->ram = ram;
mem->flags = kvm_mem_flags(mr); mem->flags = kvm_mem_flags(mr);
mem->guest_memfd = mr->ram_block->guest_memfd; mem->guest_memfd = mr->ram_block->guest_memfd;
mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host; mem->guest_memfd_offset = mem->guest_memfd >= 0 ?
(uint8_t*)ram - mr->ram_block->host : 0;
kvm_slot_init_dirty_bitmap(mem); kvm_slot_init_dirty_bitmap(mem);
err = kvm_set_user_memory_region(kml, mem, true); err = kvm_set_user_memory_region(kml, mem, true);
@ -2776,8 +2780,8 @@ static int kvm_init(AccelState *as, MachineState *ms)
kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES); kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES);
kvm_guest_memfd_supported = kvm_guest_memfd_supported =
kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) && kvm_vm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_check_extension(s, KVM_CAP_USER_MEMORY2) && kvm_vm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE); (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY); kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
@ -2934,22 +2938,32 @@ void kvm_cpu_synchronize_state(CPUState *cpu)
} }
} }
static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) static bool kvm_cpu_synchronize_put(CPUState *cpu, KvmPutState state,
const char *desc)
{ {
Error *err = NULL; Error *err = NULL;
int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err); int ret = kvm_arch_put_registers(cpu, state, &err);
if (ret) { if (ret) {
if (err) { if (err) {
error_reportf_err(err, "Restoring resisters after reset: "); error_reportf_err(err, "Restoring resisters %s: ", desc);
} else { } else {
error_report("Failed to put registers after reset: %s", error_report("Failed to put registers %s: %s", desc,
strerror(-ret)); strerror(-ret));
} }
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); return false;
vm_stop(RUN_STATE_INTERNAL_ERROR);
} }
cpu->vcpu_dirty = false; cpu->vcpu_dirty = false;
return true;
}
static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
{
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RESET_STATE, "after reset")) {
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
} }
void kvm_cpu_synchronize_post_reset(CPUState *cpu) void kvm_cpu_synchronize_post_reset(CPUState *cpu)
@ -2963,19 +2977,9 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu)
static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
{ {
Error *err = NULL; if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_FULL_STATE, "after init")) {
int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Putting registers after init: ");
} else {
error_report("Failed to put registers after init: %s",
strerror(-ret));
}
exit(1); exit(1);
} }
cpu->vcpu_dirty = false;
} }
void kvm_cpu_synchronize_post_init(CPUState *cpu) void kvm_cpu_synchronize_post_init(CPUState *cpu)
@ -3029,10 +3033,6 @@ static void kvm_eat_signals(CPUState *cpu)
if (kvm_immediate_exit) { if (kvm_immediate_exit) {
qatomic_set(&cpu->kvm_run->immediate_exit, 0); qatomic_set(&cpu->kvm_run->immediate_exit, 0);
/* Write kvm_run->immediate_exit before the cpu->exit_request
* write in kvm_cpu_exec.
*/
smp_wmb();
return; return;
} }
@ -3159,7 +3159,6 @@ int kvm_cpu_exec(CPUState *cpu)
trace_kvm_cpu_exec(); trace_kvm_cpu_exec();
if (kvm_arch_process_async_events(cpu)) { if (kvm_arch_process_async_events(cpu)) {
qatomic_set(&cpu->exit_request, 0);
return EXCP_HLT; return EXCP_HLT;
} }
@ -3170,24 +3169,16 @@ int kvm_cpu_exec(CPUState *cpu)
MemTxAttrs attrs; MemTxAttrs attrs;
if (cpu->vcpu_dirty) { if (cpu->vcpu_dirty) {
Error *err = NULL; if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RUNTIME_STATE,
ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err); "at runtime")) {
if (ret) {
if (err) {
error_reportf_err(err, "Putting registers after init: ");
} else {
error_report("Failed to put registers after init: %s",
strerror(-ret));
}
ret = -1; ret = -1;
break; break;
} }
cpu->vcpu_dirty = false;
} }
kvm_arch_pre_run(cpu, run); kvm_arch_pre_run(cpu, run);
if (qatomic_read(&cpu->exit_request)) { /* Corresponding store-release is in cpu_exit. */
if (qatomic_load_acquire(&cpu->exit_request)) {
trace_kvm_interrupt_exit_request(); trace_kvm_interrupt_exit_request();
/* /*
* KVM requires us to reenter the kernel after IO exits to complete * KVM requires us to reenter the kernel after IO exits to complete
@ -3197,13 +3188,15 @@ int kvm_cpu_exec(CPUState *cpu)
kvm_cpu_kick_self(); kvm_cpu_kick_self();
} }
/* Read cpu->exit_request before KVM_RUN reads run->immediate_exit.
* Matching barrier in kvm_eat_signals.
*/
smp_rmb();
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
/*
* After writing cpu->exit_request, cpu_exit() sends a signal that writes
* kvm->run->immediate_exit. The signal is already happening after the
* write to cpu->exit_request so, if KVM read kvm->run->immediate_exit
* as true, cpu->exit_request will always read as true.
*/
attrs = kvm_arch_post_run(cpu, run); attrs = kvm_arch_post_run(cpu, run);
#ifdef KVM_HAVE_MCE_INJECTION #ifdef KVM_HAVE_MCE_INJECTION
@ -3346,7 +3339,6 @@ int kvm_cpu_exec(CPUState *cpu)
vm_stop(RUN_STATE_INTERNAL_ERROR); vm_stop(RUN_STATE_INTERNAL_ERROR);
} }
qatomic_set(&cpu->exit_request, 0);
return ret; return ret;
} }
@ -3381,10 +3373,10 @@ int kvm_vm_ioctl(KVMState *s, unsigned long type, ...)
trace_kvm_vm_ioctl(type, arg); trace_kvm_vm_ioctl(type, arg);
accel_ioctl_begin(); accel_ioctl_begin();
ret = ioctl(s->vmfd, type, arg); ret = ioctl(s->vmfd, type, arg);
accel_ioctl_end();
if (ret == -1) { if (ret == -1) {
ret = -errno; ret = -errno;
} }
accel_ioctl_end();
return ret; return ret;
} }
@ -3421,10 +3413,10 @@ int kvm_device_ioctl(int fd, unsigned long type, ...)
trace_kvm_device_ioctl(fd, type, arg); trace_kvm_device_ioctl(fd, type, arg);
accel_ioctl_begin(); accel_ioctl_begin();
ret = ioctl(fd, type, arg); ret = ioctl(fd, type, arg);
accel_ioctl_end();
if (ret == -1) { if (ret == -1) {
ret = -errno; ret = -errno;
} }
accel_ioctl_end();
return ret; return ret;
} }
@ -3731,7 +3723,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
have_sigbus_pending = true; have_sigbus_pending = true;
pending_sigbus_addr = addr; pending_sigbus_addr = addr;
pending_sigbus_code = code; pending_sigbus_code = code;
qatomic_set(&cpu->exit_request, 1); qatomic_set(&cpu->exit_request, true);
return 0; return 0;
#else #else
return 1; return 1;

View file

@ -1,6 +1,6 @@
common_ss.add(files('accel-common.c')) common_ss.add(files('accel-common.c'))
specific_ss.add(files('accel-target.c')) specific_ss.add(files('accel-target.c'))
system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c')) system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c', 'accel-irq.c'))
user_ss.add(files('accel-user.c')) user_ss.add(files('accel-user.c'))
subdir('tcg') subdir('tcg')
@ -10,6 +10,7 @@ if have_system
subdir('kvm') subdir('kvm')
subdir('xen') subdir('xen')
subdir('stubs') subdir('stubs')
subdir('mshv')
endif endif
# qtest # qtest

399
accel/mshv/irq.c Normal file
View file

@ -0,0 +1,399 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
* Stanislav Kinsburskii <skinsburskii@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "linux/mshv.h"
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/hyperv/hvhdk_mini.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "hw/intc/ioapic.h"
#include "hw/pci/msi.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "trace.h"
#include <stdint.h>
#include <sys/ioctl.h>
#define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE)
#define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN)
static MshvMsiControl *msi_control;
static QemuMutex msi_control_mutex;
void mshv_init_msicontrol(void)
{
qemu_mutex_init(&msi_control_mutex);
msi_control = g_new0(MshvMsiControl, 1);
msi_control->gsi_routes = g_hash_table_new(g_direct_hash, g_direct_equal);
msi_control->updated = false;
}
static int set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data)
{
struct mshv_user_irq_entry *entry;
uint32_t high_addr = addr >> 32;
uint32_t low_addr = addr & 0xFFFFFFFF;
GHashTable *gsi_routes;
trace_mshv_set_msi_routing(gsi, addr, data);
if (gsi >= MSHV_MAX_MSI_ROUTES) {
error_report("gsi >= MSHV_MAX_MSI_ROUTES");
return -1;
}
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
gsi_routes = msi_control->gsi_routes;
entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
if (entry
&& entry->address_hi == high_addr
&& entry->address_lo == low_addr
&& entry->data == data)
{
/* nothing to update */
return 0;
}
/* free old entry */
g_free(entry);
/* create new entry */
entry = g_new0(struct mshv_user_irq_entry, 1);
entry->gsi = gsi;
entry->address_hi = high_addr;
entry->address_lo = low_addr;
entry->data = data;
g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), entry);
msi_control->updated = true;
}
return 0;
}
static int add_msi_routing(uint64_t addr, uint32_t data)
{
struct mshv_user_irq_entry *route_entry;
uint32_t high_addr = addr >> 32;
uint32_t low_addr = addr & 0xFFFFFFFF;
int gsi;
GHashTable *gsi_routes;
trace_mshv_add_msi_routing(addr, data);
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
/* find an empty slot */
gsi = 0;
gsi_routes = msi_control->gsi_routes;
while (gsi < MSHV_MAX_MSI_ROUTES) {
route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
if (!route_entry) {
break;
}
gsi++;
}
if (gsi >= MSHV_MAX_MSI_ROUTES) {
error_report("No empty gsi slot available");
return -1;
}
/* create new entry */
route_entry = g_new0(struct mshv_user_irq_entry, 1);
route_entry->gsi = gsi;
route_entry->address_hi = high_addr;
route_entry->address_lo = low_addr;
route_entry->data = data;
g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), route_entry);
msi_control->updated = true;
}
return gsi;
}
static int commit_msi_routing_table(int vm_fd)
{
guint len;
int i, ret;
size_t table_size;
struct mshv_user_irq_table *table;
GHashTableIter iter;
gpointer key, value;
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
if (!msi_control->updated) {
/* nothing to update */
return 0;
}
/* Calculate the size of the table */
len = g_hash_table_size(msi_control->gsi_routes);
table_size = sizeof(struct mshv_user_irq_table)
+ len * sizeof(struct mshv_user_irq_entry);
table = g_malloc0(table_size);
g_hash_table_iter_init(&iter, msi_control->gsi_routes);
i = 0;
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct mshv_user_irq_entry *entry = value;
table->entries[i] = *entry;
i++;
}
table->nr = i;
trace_mshv_commit_msi_routing_table(vm_fd, len);
ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, table);
g_free(table);
if (ret < 0) {
error_report("Failed to commit msi routing table");
return -1;
}
msi_control->updated = false;
}
return 0;
}
static int remove_msi_routing(uint32_t gsi)
{
struct mshv_user_irq_entry *route_entry;
GHashTable *gsi_routes;
trace_mshv_remove_msi_routing(gsi);
if (gsi >= MSHV_MAX_MSI_ROUTES) {
error_report("Invalid GSI: %u", gsi);
return -1;
}
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
gsi_routes = msi_control->gsi_routes;
route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
if (route_entry) {
g_hash_table_remove(gsi_routes, GINT_TO_POINTER(gsi));
g_free(route_entry);
msi_control->updated = true;
}
}
return 0;
}
/* Pass an eventfd which is to be used for injecting interrupts from userland */
static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
uint32_t flags)
{
int ret;
struct mshv_user_irqfd arg = {
.fd = fd,
.resamplefd = resample_fd,
.gsi = gsi,
.flags = flags,
};
ret = ioctl(vm_fd, MSHV_IRQFD, &arg);
if (ret < 0) {
error_report("Failed to set irqfd: gsi=%u, fd=%d", gsi, fd);
return -1;
}
return ret;
}
static int register_irqfd(int vm_fd, int event_fd, uint32_t gsi)
{
int ret;
trace_mshv_register_irqfd(vm_fd, event_fd, gsi);
ret = irqfd(vm_fd, event_fd, 0, gsi, 0);
if (ret < 0) {
error_report("Failed to register irqfd: gsi=%u", gsi);
return -1;
}
return 0;
}
static int register_irqfd_with_resample(int vm_fd, int event_fd,
int resample_fd, uint32_t gsi)
{
int ret;
uint32_t flags = MSHV_IRQFD_RESAMPLE_FLAG;
ret = irqfd(vm_fd, event_fd, resample_fd, gsi, flags);
if (ret < 0) {
error_report("Failed to register irqfd with resample: gsi=%u", gsi);
return -errno;
}
return 0;
}
static int unregister_irqfd(int vm_fd, int event_fd, uint32_t gsi)
{
int ret;
uint32_t flags = MSHV_IRQFD_BIT_DEASSIGN_FLAG;
ret = irqfd(vm_fd, event_fd, 0, gsi, flags);
if (ret < 0) {
error_report("Failed to unregister irqfd: gsi=%u", gsi);
return -errno;
}
return 0;
}
static int irqchip_update_irqfd_notifier_gsi(const EventNotifier *event,
const EventNotifier *resample,
int virq, bool add)
{
int fd = event_notifier_get_fd(event);
int rfd = resample ? event_notifier_get_fd(resample) : -1;
int vm_fd = mshv_state->vm;
trace_mshv_irqchip_update_irqfd_notifier_gsi(fd, rfd, virq, add);
if (!add) {
return unregister_irqfd(vm_fd, fd, virq);
}
if (rfd > 0) {
return register_irqfd_with_resample(vm_fd, fd, rfd, virq);
}
return register_irqfd(vm_fd, fd, virq);
}
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
{
MSIMessage msg = { 0, 0 };
int virq = 0;
if (pci_available && dev) {
msg = pci_get_msi_message(dev, vector);
virq = add_msi_routing(msg.address, le32_to_cpu(msg.data));
}
return virq;
}
void mshv_irqchip_release_virq(int virq)
{
remove_msi_routing(virq);
}
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
{
int ret;
ret = set_msi_routing(virq, msg.address, le32_to_cpu(msg.data));
if (ret < 0) {
error_report("Failed to set msi routing");
return -1;
}
return 0;
}
int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector,
uint32_t vp_index, bool logical_dest_mode,
bool level_triggered)
{
int ret;
int vm_fd = mshv_state->vm;
if (vector == 0) {
warn_report("Ignoring request for interrupt vector 0");
return 0;
}
union hv_interrupt_control control = {
.interrupt_type = interrupt_type,
.level_triggered = level_triggered,
.logical_dest_mode = logical_dest_mode,
.rsvd = 0,
};
struct hv_input_assert_virtual_interrupt arg = {0};
arg.control = control;
arg.dest_addr = (uint64_t)vp_index;
arg.vector = vector;
struct mshv_root_hvcall args = {0};
args.code = HVCALL_ASSERT_VIRTUAL_INTERRUPT;
args.in_sz = sizeof(arg);
args.in_ptr = (uint64_t)&arg;
ret = mshv_hvcall(vm_fd, &args);
if (ret < 0) {
error_report("Failed to request interrupt");
return -errno;
}
return 0;
}
void mshv_irqchip_commit_routes(void)
{
int ret;
int vm_fd = mshv_state->vm;
ret = commit_msi_routing_table(vm_fd);
if (ret < 0) {
error_report("Failed to commit msi routing table");
abort();
}
}
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event,
const EventNotifier *resample,
int virq)
{
return irqchip_update_irqfd_notifier_gsi(event, resample, virq, true);
}
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *event,
int virq)
{
return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false);
}
int mshv_reserve_ioapic_msi_routes(int vm_fd)
{
int ret, gsi;
/*
* Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy
* peripherals with MSI-X devices
*/
for (gsi = 0; gsi < IOAPIC_NUM_PINS; gsi++) {
ret = add_msi_routing(0, 0);
if (ret < 0) {
error_report("Failed to reserve GSI %d", gsi);
return -1;
}
}
ret = commit_msi_routing_table(vm_fd);
if (ret < 0) {
error_report("Failed to commit reserved IOAPIC MSI routes");
return -1;
}
return 0;
}

563
accel/mshv/mem.c Normal file
View file

@ -0,0 +1,563 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors:
* Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "qemu/osdep.h"
#include "qemu/lockable.h"
#include "qemu/error-report.h"
#include "qemu/rcu.h"
#include "linux/mshv.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "exec/memattrs.h"
#include <sys/ioctl.h>
#include "trace.h"
typedef struct SlotsRCUReclaim {
struct rcu_head rcu;
GList *old_head;
MshvMemorySlot *removed_slot;
} SlotsRCUReclaim;
static void rcu_reclaim_slotlist(struct rcu_head *rcu)
{
SlotsRCUReclaim *r = container_of(rcu, SlotsRCUReclaim, rcu);
g_list_free(r->old_head);
g_free(r->removed_slot);
g_free(r);
}
static void publish_slots(GList *new_head, GList *old_head,
MshvMemorySlot *removed_slot)
{
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
qatomic_store_release(&manager->slots, new_head);
SlotsRCUReclaim *r = g_new(SlotsRCUReclaim, 1);
r->old_head = old_head;
r->removed_slot = removed_slot;
call_rcu1(&r->rcu, rcu_reclaim_slotlist);
}
/* Needs to be called with mshv_state->msm.mutex held */
static int remove_slot(MshvMemorySlot *slot)
{
GList *old_head, *new_head;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
old_head = qatomic_load_acquire(&manager->slots);
if (!g_list_find(old_head, slot)) {
error_report("slot requested for removal not found");
return -1;
}
new_head = g_list_copy(old_head);
new_head = g_list_remove(new_head, slot);
manager->n_slots--;
publish_slots(new_head, old_head, slot);
return 0;
}
/* Needs to be called with mshv_state->msm.mutex held */
static MshvMemorySlot *append_slot(uint64_t gpa, uint64_t userspace_addr,
uint64_t size, bool readonly)
{
GList *old_head, *new_head;
MshvMemorySlot *slot;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
old_head = qatomic_load_acquire(&manager->slots);
if (manager->n_slots >= MSHV_MAX_MEM_SLOTS) {
error_report("no free memory slots available");
return NULL;
}
slot = g_new0(MshvMemorySlot, 1);
slot->guest_phys_addr = gpa;
slot->userspace_addr = userspace_addr;
slot->memory_size = size;
slot->readonly = readonly;
new_head = g_list_copy(old_head);
new_head = g_list_append(new_head, slot);
manager->n_slots++;
publish_slots(new_head, old_head, NULL);
return slot;
}
static int slot_overlaps(const MshvMemorySlot *slot1,
const MshvMemorySlot *slot2)
{
uint64_t start_1 = slot1->userspace_addr,
start_2 = slot2->userspace_addr;
size_t len_1 = slot1->memory_size,
len_2 = slot2->memory_size;
if (slot1 == slot2) {
return -1;
}
return ranges_overlap(start_1, len_1, start_2, len_2) ? 0 : -1;
}
static bool is_mapped(MshvMemorySlot *slot)
{
/* Subsequent reads of mapped field see a fully-initialized slot */
return qatomic_load_acquire(&slot->mapped);
}
/*
* Find slot that is:
* - overlapping in userspace
* - currently mapped in the guest
*
* Needs to be called with mshv_state->msm.mutex or RCU read lock held.
*/
static MshvMemorySlot *find_overlap_mem_slot(GList *head, MshvMemorySlot *slot)
{
GList *found;
MshvMemorySlot *overlap_slot;
found = g_list_find_custom(head, slot, (GCompareFunc) slot_overlaps);
if (!found) {
return NULL;
}
overlap_slot = found->data;
if (!overlap_slot || !is_mapped(overlap_slot)) {
return NULL;
}
return overlap_slot;
}
static int set_guest_memory(int vm_fd,
const struct mshv_user_mem_region *region)
{
int ret;
ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region);
if (ret < 0) {
error_report("failed to set guest memory: %s", strerror(errno));
return -1;
}
return 0;
}
static int map_or_unmap(int vm_fd, const MshvMemorySlot *slot, bool map)
{
struct mshv_user_mem_region region = {0};
region.guest_pfn = slot->guest_phys_addr >> MSHV_PAGE_SHIFT;
region.size = slot->memory_size;
region.userspace_addr = slot->userspace_addr;
if (!map) {
region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP);
trace_mshv_unmap_memory(slot->userspace_addr, slot->guest_phys_addr,
slot->memory_size);
return set_guest_memory(vm_fd, &region);
}
region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE);
if (!slot->readonly) {
region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE);
}
trace_mshv_map_memory(slot->userspace_addr, slot->guest_phys_addr,
slot->memory_size);
return set_guest_memory(vm_fd, &region);
}
static int slot_matches_region(const MshvMemorySlot *slot1,
const MshvMemorySlot *slot2)
{
return (slot1->guest_phys_addr == slot2->guest_phys_addr &&
slot1->userspace_addr == slot2->userspace_addr &&
slot1->memory_size == slot2->memory_size) ? 0 : -1;
}
/* Needs to be called with mshv_state->msm.mutex held */
static MshvMemorySlot *find_mem_slot_by_region(uint64_t gpa, uint64_t size,
uint64_t userspace_addr)
{
MshvMemorySlot ref_slot = {
.guest_phys_addr = gpa,
.userspace_addr = userspace_addr,
.memory_size = size,
};
GList *found;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
found = g_list_find_custom(manager->slots, &ref_slot,
(GCompareFunc) slot_matches_region);
return found ? found->data : NULL;
}
static int slot_covers_gpa(const MshvMemorySlot *slot, uint64_t *gpa_p)
{
uint64_t gpa_offset, gpa = *gpa_p;
gpa_offset = gpa - slot->guest_phys_addr;
return (slot->guest_phys_addr <= gpa && gpa_offset < slot->memory_size)
? 0 : -1;
}
/* Needs to be called with mshv_state->msm.mutex or RCU read lock held */
static MshvMemorySlot *find_mem_slot_by_gpa(GList *head, uint64_t gpa)
{
GList *found;
MshvMemorySlot *slot;
trace_mshv_find_slot_by_gpa(gpa);
found = g_list_find_custom(head, &gpa, (GCompareFunc) slot_covers_gpa);
if (found) {
slot = found->data;
trace_mshv_found_slot(slot->userspace_addr, slot->guest_phys_addr,
slot->memory_size);
return slot;
}
return NULL;
}
/* Needs to be called with mshv_state->msm.mutex held */
static void set_mapped(MshvMemorySlot *slot, bool mapped)
{
/* prior writes to mapped field becomes visible before readers see slot */
qatomic_store_release(&slot->mapped, mapped);
}
MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa)
{
MshvMemorySlot *gpa_slot, *overlap_slot;
GList *head;
int ret;
MshvMemorySlotManager *manager = &mshv_state->msm;
/* fast path, called often by unmapped_gpa vm exit */
WITH_RCU_READ_LOCK_GUARD() {
assert(manager);
head = qatomic_load_acquire(&manager->slots);
/* return early if no slot is found */
gpa_slot = find_mem_slot_by_gpa(head, gpa);
if (gpa_slot == NULL) {
return MshvRemapNoMapping;
}
/* return early if no overlapping slot is found */
overlap_slot = find_overlap_mem_slot(head, gpa_slot);
if (overlap_slot == NULL) {
return MshvRemapNoOverlap;
}
}
/*
* We'll modify the mapping list, so we need to upgrade to mutex and
* recheck.
*/
assert(manager);
QEMU_LOCK_GUARD(&manager->mutex);
/* return early if no slot is found */
gpa_slot = find_mem_slot_by_gpa(manager->slots, gpa);
if (gpa_slot == NULL) {
return MshvRemapNoMapping;
}
/* return early if no overlapping slot is found */
overlap_slot = find_overlap_mem_slot(manager->slots, gpa_slot);
if (overlap_slot == NULL) {
return MshvRemapNoOverlap;
}
/* unmap overlapping slot */
ret = map_or_unmap(vm_fd, overlap_slot, false);
if (ret < 0) {
error_report("failed to unmap overlap region");
abort();
}
set_mapped(overlap_slot, false);
warn_report("mapped out userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
overlap_slot->userspace_addr,
overlap_slot->guest_phys_addr,
overlap_slot->memory_size);
/* map region for gpa */
ret = map_or_unmap(vm_fd, gpa_slot, true);
if (ret < 0) {
error_report("failed to map new region");
abort();
}
set_mapped(gpa_slot, true);
warn_report("mapped in userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
gpa_slot->userspace_addr, gpa_slot->guest_phys_addr,
gpa_slot->memory_size);
return MshvRemapOk;
}
static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size,
uint8_t *data)
{
warn_report("read from unmapped mmio region gpa=0x%lx size=%lu", gpa, size);
if (size == 0 || size > 8) {
error_report("invalid size %lu for reading from unmapped mmio region",
size);
return -1;
}
memset(data, 0xFF, size);
return 0;
}
int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size,
bool is_secure_mode, bool instruction_fetch)
{
int ret;
MemTxAttrs memattr = { .secure = is_secure_mode };
if (instruction_fetch) {
trace_mshv_insn_fetch(gpa, size);
} else {
trace_mshv_mem_read(gpa, size);
}
ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data,
size, false);
if (ret == MEMTX_OK) {
return 0;
}
if (ret == MEMTX_DECODE_ERROR) {
return handle_unmapped_mmio_region_read(gpa, size, data);
}
error_report("failed to read guest memory at 0x%lx", gpa);
return -1;
}
int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
bool is_secure_mode)
{
int ret;
MemTxAttrs memattr = { .secure = is_secure_mode };
trace_mshv_mem_write(gpa, size);
ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data,
size, true);
if (ret == MEMTX_OK) {
return 0;
}
if (ret == MEMTX_DECODE_ERROR) {
warn_report("write to unmapped mmio region gpa=0x%lx size=%lu", gpa,
size);
return 0;
}
error_report("Failed to write guest memory");
return -1;
}
static int tracked_unmap(int vm_fd, uint64_t gpa, uint64_t size,
uint64_t userspace_addr)
{
int ret;
MshvMemorySlot *slot;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
QEMU_LOCK_GUARD(&manager->mutex);
slot = find_mem_slot_by_region(gpa, size, userspace_addr);
if (!slot) {
trace_mshv_skip_unset_mem(userspace_addr, gpa, size);
/* no work to do */
return 0;
}
if (!is_mapped(slot)) {
/* remove slot, no need to unmap */
return remove_slot(slot);
}
ret = map_or_unmap(vm_fd, slot, false);
if (ret < 0) {
error_report("failed to unmap memory region");
return ret;
}
return remove_slot(slot);
}
static int tracked_map(int vm_fd, uint64_t gpa, uint64_t size, bool readonly,
uint64_t userspace_addr)
{
MshvMemorySlot *slot, *overlap_slot;
int ret;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
QEMU_LOCK_GUARD(&manager->mutex);
slot = find_mem_slot_by_region(gpa, size, userspace_addr);
if (slot) {
error_report("memory region already mapped at gpa=0x%lx, "
"userspace_addr=0x%lx, size=0x%lx",
slot->guest_phys_addr, slot->userspace_addr,
slot->memory_size);
return -1;
}
slot = append_slot(gpa, userspace_addr, size, readonly);
overlap_slot = find_overlap_mem_slot(manager->slots, slot);
if (overlap_slot) {
trace_mshv_remap_attempt(slot->userspace_addr,
slot->guest_phys_addr,
slot->memory_size);
warn_report("attempt to map region [0x%lx-0x%lx], while "
"[0x%lx-0x%lx] is already mapped in the guest",
userspace_addr, userspace_addr + size - 1,
overlap_slot->userspace_addr,
overlap_slot->userspace_addr +
overlap_slot->memory_size - 1);
/* do not register mem slot in hv, but record for later swap-in */
set_mapped(slot, false);
return 0;
}
ret = map_or_unmap(vm_fd, slot, true);
if (ret < 0) {
error_report("failed to map memory region");
return -1;
}
set_mapped(slot, true);
return 0;
}
static int set_memory(uint64_t gpa, uint64_t size, bool readonly,
uint64_t userspace_addr, bool add)
{
int vm_fd = mshv_state->vm;
if (add) {
return tracked_map(vm_fd, gpa, size, readonly, userspace_addr);
}
return tracked_unmap(vm_fd, gpa, size, userspace_addr);
}
/*
* Calculate and align the start address and the size of the section.
* Return the size. If the size is 0, the aligned section is empty.
*/
static hwaddr align_section(MemoryRegionSection *section, hwaddr *start)
{
hwaddr size = int128_get64(section->size);
hwaddr delta, aligned;
/*
* works in page size chunks, but the function may be called
* with sub-page size and unaligned start address. Pad the start
* address to next and truncate size to previous page boundary.
*/
aligned = ROUND_UP(section->offset_within_address_space,
qemu_real_host_page_size());
delta = aligned - section->offset_within_address_space;
*start = aligned;
if (delta > size) {
return 0;
}
return (size - delta) & qemu_real_host_page_mask();
}
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add)
{
int ret = 0;
MemoryRegion *area = section->mr;
bool writable = !area->readonly && !area->rom_device;
hwaddr start_addr, mr_offset, size;
void *ram;
size = align_section(section, &start_addr);
trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
size = align_section(section, &start_addr);
trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
/*
* If the memory device is a writable non-ram area, we do not
* want to map it into the guest memory. If it is not a ROM device,
* we want to remove mshv memory mapping, so accesses will trap.
*/
if (!memory_region_is_ram(area)) {
if (writable) {
return;
} else if (!area->romd_mode) {
add = false;
}
}
if (!size) {
return;
}
mr_offset = section->offset_within_region + start_addr -
section->offset_within_address_space;
ram = memory_region_get_ram_ptr(area) + mr_offset;
ret = set_memory(start_addr, size, !writable, (uint64_t)ram, add);
if (ret < 0) {
error_report("failed to set memory region");
abort();
}
}
void mshv_init_memory_slot_manager(MshvState *mshv_state)
{
MshvMemorySlotManager *manager;
assert(mshv_state);
manager = &mshv_state->msm;
manager->n_slots = 0;
manager->slots = NULL;
qemu_mutex_init(&manager->mutex);
}

9
accel/mshv/meson.build Normal file
View file

@ -0,0 +1,9 @@
mshv_ss = ss.source_set()
mshv_ss.add(if_true: files(
'irq.c',
'mem.c',
'msr.c',
'mshv-all.c'
))
specific_ss.add_all(when: 'CONFIG_MSHV', if_true: mshv_ss)

730
accel/mshv/mshv-all.c Normal file
View file

@ -0,0 +1,730 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors:
* Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
* Jinank Jain <jinankjain@microsoft.com>
* Wei Liu <liuwe@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/event_notifier.h"
#include "qemu/module.h"
#include "qemu/main-loop.h"
#include "hw/boards.h"
#include "hw/hyperv/hvhdk.h"
#include "hw/hyperv/hvhdk_mini.h"
#include "hw/hyperv/hvgdk.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "linux/mshv.h"
#include "qemu/accel.h"
#include "qemu/guest-random.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/cpus.h"
#include "system/runstate.h"
#include "system/accel-blocker.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "system/reset.h"
#include "trace.h"
#include <err.h>
#include <stdint.h>
#include <sys/ioctl.h>
#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL)
bool mshv_allowed;
MshvState *mshv_state;
static int init_mshv(int *mshv_fd)
{
int fd = open("/dev/mshv", O_RDWR | O_CLOEXEC);
if (fd < 0) {
error_report("Failed to open /dev/mshv: %s", strerror(errno));
return -1;
}
*mshv_fd = fd;
return 0;
}
/* freeze 1 to pause, 0 to resume */
static int set_time_freeze(int vm_fd, int freeze)
{
int ret;
struct hv_input_set_partition_property in = {0};
in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE;
in.property_value = freeze;
struct mshv_root_hvcall args = {0};
args.code = HVCALL_SET_PARTITION_PROPERTY;
args.in_sz = sizeof(in);
args.in_ptr = (uint64_t)&in;
ret = mshv_hvcall(vm_fd, &args);
if (ret < 0) {
error_report("Failed to set time freeze");
return -1;
}
return 0;
}
static int pause_vm(int vm_fd)
{
int ret;
ret = set_time_freeze(vm_fd, 1);
if (ret < 0) {
error_report("Failed to pause partition: %s", strerror(errno));
return -1;
}
return 0;
}
static int resume_vm(int vm_fd)
{
int ret;
ret = set_time_freeze(vm_fd, 0);
if (ret < 0) {
error_report("Failed to resume partition: %s", strerror(errno));
return -1;
}
return 0;
}
static int create_partition(int mshv_fd, int *vm_fd)
{
int ret;
struct mshv_create_partition args = {0};
/* Initialize pt_flags with the desired features */
uint64_t pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
(1ULL << MSHV_PT_BIT_X2APIC) |
(1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES);
/* Set default isolation type */
uint64_t pt_isolation = MSHV_PT_ISOLATION_NONE;
args.pt_flags = pt_flags;
args.pt_isolation = pt_isolation;
ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args);
if (ret < 0) {
error_report("Failed to create partition: %s", strerror(errno));
return -1;
}
*vm_fd = ret;
return 0;
}
static int set_synthetic_proc_features(int vm_fd)
{
int ret;
struct hv_input_set_partition_property in = {0};
union hv_partition_synthetic_processor_features features = {0};
/* Access the bitfield and set the desired features */
features.hypervisor_present = 1;
features.hv1 = 1;
features.access_partition_reference_counter = 1;
features.access_synic_regs = 1;
features.access_synthetic_timer_regs = 1;
features.access_partition_reference_tsc = 1;
features.access_frequency_regs = 1;
features.access_intr_ctrl_regs = 1;
features.access_vp_index = 1;
features.access_hypercall_regs = 1;
features.tb_flush_hypercalls = 1;
features.synthetic_cluster_ipi = 1;
features.direct_synthetic_timers = 1;
mshv_arch_amend_proc_features(&features);
in.property_code = HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES;
in.property_value = features.as_uint64[0];
struct mshv_root_hvcall args = {0};
args.code = HVCALL_SET_PARTITION_PROPERTY;
args.in_sz = sizeof(in);
args.in_ptr = (uint64_t)&in;
trace_mshv_hvcall_args("synthetic_proc_features", args.code, args.in_sz);
ret = mshv_hvcall(vm_fd, &args);
if (ret < 0) {
error_report("Failed to set synthethic proc features");
return -errno;
}
return 0;
}
static int initialize_vm(int vm_fd)
{
int ret = ioctl(vm_fd, MSHV_INITIALIZE_PARTITION);
if (ret < 0) {
error_report("Failed to initialize partition: %s", strerror(errno));
return -1;
}
return 0;
}
static int create_vm(int mshv_fd, int *vm_fd)
{
int ret = create_partition(mshv_fd, vm_fd);
if (ret < 0) {
return -1;
}
ret = set_synthetic_proc_features(*vm_fd);
if (ret < 0) {
return -1;
}
ret = initialize_vm(*vm_fd);
if (ret < 0) {
return -1;
}
ret = mshv_reserve_ioapic_msi_routes(*vm_fd);
if (ret < 0) {
return -1;
}
ret = mshv_arch_post_init_vm(*vm_fd);
if (ret < 0) {
return -1;
}
/* Always create a frozen partition */
pause_vm(*vm_fd);
return 0;
}
static void mem_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
MshvMemoryListener *mml;
mml = container_of(listener, MshvMemoryListener, listener);
memory_region_ref(section->mr);
mshv_set_phys_mem(mml, section, true);
}
static void mem_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
MshvMemoryListener *mml;
mml = container_of(listener, MshvMemoryListener, listener);
mshv_set_phys_mem(mml, section, false);
memory_region_unref(section->mr);
}
typedef enum {
DATAMATCH_NONE,
DATAMATCH_U32,
DATAMATCH_U64,
} DatamatchTag;
typedef struct {
DatamatchTag tag;
union {
uint32_t u32;
uint64_t u64;
} value;
} Datamatch;
/* flags: determine whether to de/assign */
static int ioeventfd(int vm_fd, int event_fd, uint64_t addr, Datamatch dm,
uint32_t flags)
{
struct mshv_user_ioeventfd args = {0};
args.fd = event_fd;
args.addr = addr;
args.flags = flags;
if (dm.tag == DATAMATCH_NONE) {
args.datamatch = 0;
} else {
flags |= BIT(MSHV_IOEVENTFD_BIT_DATAMATCH);
args.flags = flags;
if (dm.tag == DATAMATCH_U64) {
args.len = sizeof(uint64_t);
args.datamatch = dm.value.u64;
} else {
args.len = sizeof(uint32_t);
args.datamatch = dm.value.u32;
}
}
return ioctl(vm_fd, MSHV_IOEVENTFD, &args);
}
static int unregister_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr)
{
uint32_t flags = 0;
Datamatch dm = {0};
flags |= BIT(MSHV_IOEVENTFD_BIT_DEASSIGN);
dm.tag = DATAMATCH_NONE;
return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags);
}
static int register_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr,
uint64_t val, bool is_64bit, bool is_datamatch)
{
uint32_t flags = 0;
Datamatch dm = {0};
if (!is_datamatch) {
dm.tag = DATAMATCH_NONE;
} else if (is_64bit) {
dm.tag = DATAMATCH_U64;
dm.value.u64 = val;
} else {
dm.tag = DATAMATCH_U32;
dm.value.u32 = val;
}
return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags);
}
static void mem_ioeventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
bool match_data, uint64_t data,
EventNotifier *e)
{
int fd = event_notifier_get_fd(e);
int ret;
bool is_64 = int128_get64(section->size) == 8;
uint64_t addr = section->offset_within_address_space;
trace_mshv_mem_ioeventfd_add(addr, int128_get64(section->size), data);
ret = register_ioevent(mshv_state->vm, fd, addr, data, is_64, match_data);
if (ret < 0) {
error_report("Failed to register ioeventfd: %s (%d)", strerror(-ret),
-ret);
abort();
}
}
static void mem_ioeventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
bool match_data, uint64_t data,
EventNotifier *e)
{
int fd = event_notifier_get_fd(e);
int ret;
uint64_t addr = section->offset_within_address_space;
trace_mshv_mem_ioeventfd_del(section->offset_within_address_space,
int128_get64(section->size), data);
ret = unregister_ioevent(mshv_state->vm, fd, addr);
if (ret < 0) {
error_report("Failed to unregister ioeventfd: %s (%d)", strerror(-ret),
-ret);
abort();
}
}
static MemoryListener mshv_memory_listener = {
.name = "mshv",
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
.region_add = mem_region_add,
.region_del = mem_region_del,
.eventfd_add = mem_ioeventfd_add,
.eventfd_del = mem_ioeventfd_del,
};
static MemoryListener mshv_io_listener = {
.name = "mshv", .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND,
/* MSHV does not support PIO eventfd */
};
static void register_mshv_memory_listener(MshvState *s, MshvMemoryListener *mml,
AddressSpace *as, int as_id,
const char *name)
{
int i;
mml->listener = mshv_memory_listener;
mml->listener.name = name;
memory_listener_register(&mml->listener, as);
for (i = 0; i < s->nr_as; ++i) {
if (!s->as[i].as) {
s->as[i].as = as;
s->as[i].ml = mml;
break;
}
}
}
int mshv_hvcall(int fd, const struct mshv_root_hvcall *args)
{
int ret = 0;
ret = ioctl(fd, MSHV_ROOT_HVCALL, args);
if (ret < 0) {
error_report("Failed to perform hvcall: %s", strerror(errno));
return -1;
}
return ret;
}
static int mshv_init_vcpu(CPUState *cpu)
{
int vm_fd = mshv_state->vm;
uint8_t vp_index = cpu->cpu_index;
int ret;
cpu->accel = g_new0(AccelCPUState, 1);
mshv_arch_init_vcpu(cpu);
ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
if (ret < 0) {
return -1;
}
cpu->accel->dirty = true;
return 0;
}
static int mshv_init(AccelState *as, MachineState *ms)
{
MshvState *s;
int mshv_fd, vm_fd, ret;
if (mshv_state) {
warn_report("MSHV accelerator already initialized");
return 0;
}
s = MSHV_STATE(as);
accel_blocker_init();
s->vm = 0;
ret = init_mshv(&mshv_fd);
if (ret < 0) {
return -1;
}
mshv_init_mmio_emu();
mshv_init_msicontrol();
mshv_init_memory_slot_manager(s);
ret = create_vm(mshv_fd, &vm_fd);
if (ret < 0) {
close(mshv_fd);
return -1;
}
ret = resume_vm(vm_fd);
if (ret < 0) {
close(mshv_fd);
close(vm_fd);
return -1;
}
s->vm = vm_fd;
s->fd = mshv_fd;
s->nr_as = 1;
s->as = g_new0(MshvAddressSpace, s->nr_as);
mshv_state = s;
register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory,
0, "mshv-memory");
memory_listener_register(&mshv_io_listener, &address_space_io);
return 0;
}
static int mshv_destroy_vcpu(CPUState *cpu)
{
int cpu_fd = mshv_vcpufd(cpu);
int vm_fd = mshv_state->vm;
mshv_remove_vcpu(vm_fd, cpu_fd);
mshv_vcpufd(cpu) = 0;
mshv_arch_destroy_vcpu(cpu);
g_clear_pointer(&cpu->accel, g_free);
return 0;
}
static int mshv_cpu_exec(CPUState *cpu)
{
hv_message mshv_msg;
enum MshvVmExit exit_reason;
int ret = 0;
bql_unlock();
cpu_exec_start(cpu);
do {
if (cpu->accel->dirty) {
ret = mshv_arch_put_registers(cpu);
if (ret) {
error_report("Failed to put registers after init: %s",
strerror(-ret));
ret = -1;
break;
}
cpu->accel->dirty = false;
}
ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason);
if (ret < 0) {
error_report("Failed to run on vcpu %d", cpu->cpu_index);
abort();
}
switch (exit_reason) {
case MshvVmExitIgnore:
break;
default:
ret = EXCP_INTERRUPT;
break;
}
} while (ret == 0);
cpu_exec_end(cpu);
bql_lock();
if (ret < 0) {
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
return ret;
}
/*
* The signal handler is triggered when QEMU's main thread receives a SIG_IPI
* (SIGUSR1). This signal causes the current CPU thread to be kicked, forcing a
* VM exit on the CPU. The VM exit generates an exit reason that breaks the loop
* (see mshv_cpu_exec). If the exit is due to a Ctrl+A+x command, the system
* will shut down. For other cases, the system will continue running.
*/
static void sa_ipi_handler(int sig)
{
/* TODO: call IOCTL to set_immediate_exit, once implemented. */
qemu_cpu_kick_self();
}
static void init_signal(CPUState *cpu)
{
/* init cpu signals */
struct sigaction sigact;
sigset_t set;
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = sa_ipi_handler;
sigaction(SIG_IPI, &sigact, NULL);
pthread_sigmask(SIG_BLOCK, NULL, &set);
sigdelset(&set, SIG_IPI);
pthread_sigmask(SIG_SETMASK, &set, NULL);
}
static void *mshv_vcpu_thread(void *arg)
{
CPUState *cpu = arg;
int ret;
rcu_register_thread();
bql_lock();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
current_cpu = cpu;
ret = mshv_init_vcpu(cpu);
if (ret < 0) {
error_report("Failed to init vcpu %d", cpu->cpu_index);
goto cleanup;
}
init_signal(cpu);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
mshv_cpu_exec(cpu);
}
} while (!cpu->unplug || cpu_can_run(cpu));
mshv_destroy_vcpu(cpu);
cleanup:
cpu_thread_signal_destroyed(cpu);
bql_unlock();
rcu_unregister_thread();
return NULL;
}
static void mshv_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/MSHV",
cpu->cpu_index);
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
trace_mshv_start_vcpu_thread(thread_name, cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, mshv_vcpu_thread, cpu,
QEMU_THREAD_JOINABLE);
}
static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
int ret = mshv_arch_put_registers(cpu);
if (ret < 0) {
error_report("Failed to put registers after init: %s", strerror(-ret));
abort();
}
cpu->accel->dirty = false;
}
static void mshv_cpu_synchronize_post_init(CPUState *cpu)
{
run_on_cpu(cpu, do_mshv_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
}
static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
{
int ret = mshv_arch_put_registers(cpu);
if (ret) {
error_report("Failed to put registers after reset: %s",
strerror(-ret));
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
cpu->accel->dirty = false;
}
static void do_mshv_cpu_synchronize_pre_loadvm(CPUState *cpu,
run_on_cpu_data arg)
{
cpu->accel->dirty = true;
}
static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
run_on_cpu(cpu, do_mshv_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
}
static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->accel->dirty) {
int ret = mshv_load_regs(cpu);
if (ret < 0) {
error_report("Failed to load registers for vcpu %d",
cpu->cpu_index);
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
cpu->accel->dirty = true;
}
}
static void mshv_cpu_synchronize(CPUState *cpu)
{
if (!cpu->accel->dirty) {
run_on_cpu(cpu, do_mshv_cpu_synchronize, RUN_ON_CPU_NULL);
}
}
static bool mshv_cpus_are_resettable(void)
{
return false;
}
static void mshv_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "MSHV";
ac->init_machine = mshv_init;
ac->allowed = &mshv_allowed;
}
static void mshv_accel_instance_init(Object *obj)
{
MshvState *s = MSHV_STATE(obj);
s->vm = 0;
}
static const TypeInfo mshv_accel_type = {
.name = TYPE_MSHV_ACCEL,
.parent = TYPE_ACCEL,
.instance_init = mshv_accel_instance_init,
.class_init = mshv_accel_class_init,
.instance_size = sizeof(MshvState),
};
static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = mshv_start_vcpu_thread;
ops->synchronize_post_init = mshv_cpu_synchronize_post_init;
ops->synchronize_post_reset = mshv_cpu_synchronize_post_reset;
ops->synchronize_state = mshv_cpu_synchronize;
ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm;
ops->cpus_are_resettable = mshv_cpus_are_resettable;
ops->handle_interrupt = generic_handle_interrupt;
}
static const TypeInfo mshv_accel_ops_type = {
.name = ACCEL_OPS_NAME("mshv"),
.parent = TYPE_ACCEL_OPS,
.class_init = mshv_accel_ops_class_init,
.abstract = true,
};
static void mshv_type_init(void)
{
type_register_static(&mshv_accel_type);
type_register_static(&mshv_accel_ops_type);
}
type_init(mshv_type_init);

375
accel/mshv/msr.c Normal file
View file

@ -0,0 +1,375 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors: Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "linux/mshv.h"
#include "qemu/error-report.h"
static uint32_t supported_msrs[64] = {
IA32_MSR_TSC,
IA32_MSR_EFER,
IA32_MSR_KERNEL_GS_BASE,
IA32_MSR_APIC_BASE,
IA32_MSR_PAT,
IA32_MSR_SYSENTER_CS,
IA32_MSR_SYSENTER_ESP,
IA32_MSR_SYSENTER_EIP,
IA32_MSR_STAR,
IA32_MSR_LSTAR,
IA32_MSR_CSTAR,
IA32_MSR_SFMASK,
IA32_MSR_MTRR_DEF_TYPE,
IA32_MSR_MTRR_PHYSBASE0,
IA32_MSR_MTRR_PHYSMASK0,
IA32_MSR_MTRR_PHYSBASE1,
IA32_MSR_MTRR_PHYSMASK1,
IA32_MSR_MTRR_PHYSBASE2,
IA32_MSR_MTRR_PHYSMASK2,
IA32_MSR_MTRR_PHYSBASE3,
IA32_MSR_MTRR_PHYSMASK3,
IA32_MSR_MTRR_PHYSBASE4,
IA32_MSR_MTRR_PHYSMASK4,
IA32_MSR_MTRR_PHYSBASE5,
IA32_MSR_MTRR_PHYSMASK5,
IA32_MSR_MTRR_PHYSBASE6,
IA32_MSR_MTRR_PHYSMASK6,
IA32_MSR_MTRR_PHYSBASE7,
IA32_MSR_MTRR_PHYSMASK7,
IA32_MSR_MTRR_FIX64K_00000,
IA32_MSR_MTRR_FIX16K_80000,
IA32_MSR_MTRR_FIX16K_A0000,
IA32_MSR_MTRR_FIX4K_C0000,
IA32_MSR_MTRR_FIX4K_C8000,
IA32_MSR_MTRR_FIX4K_D0000,
IA32_MSR_MTRR_FIX4K_D8000,
IA32_MSR_MTRR_FIX4K_E0000,
IA32_MSR_MTRR_FIX4K_E8000,
IA32_MSR_MTRR_FIX4K_F0000,
IA32_MSR_MTRR_FIX4K_F8000,
IA32_MSR_TSC_AUX,
IA32_MSR_DEBUG_CTL,
HV_X64_MSR_GUEST_OS_ID,
HV_X64_MSR_SINT0,
HV_X64_MSR_SINT1,
HV_X64_MSR_SINT2,
HV_X64_MSR_SINT3,
HV_X64_MSR_SINT4,
HV_X64_MSR_SINT5,
HV_X64_MSR_SINT6,
HV_X64_MSR_SINT7,
HV_X64_MSR_SINT8,
HV_X64_MSR_SINT9,
HV_X64_MSR_SINT10,
HV_X64_MSR_SINT11,
HV_X64_MSR_SINT12,
HV_X64_MSR_SINT13,
HV_X64_MSR_SINT14,
HV_X64_MSR_SINT15,
HV_X64_MSR_SCONTROL,
HV_X64_MSR_SIEFP,
HV_X64_MSR_SIMP,
HV_X64_MSR_REFERENCE_TSC,
HV_X64_MSR_EOM,
};
static const size_t msr_count = ARRAY_SIZE(supported_msrs);
static int compare_msr_index(const void *a, const void *b)
{
return *(uint32_t *)a - *(uint32_t *)b;
}
__attribute__((constructor))
static void init_sorted_msr_map(void)
{
qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index);
}
static int mshv_is_supported_msr(uint32_t msr)
{
return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t),
compare_msr_index) != NULL;
}
static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg)
{
switch (msr) {
case IA32_MSR_TSC:
*hv_reg = HV_X64_REGISTER_TSC;
return 0;
case IA32_MSR_EFER:
*hv_reg = HV_X64_REGISTER_EFER;
return 0;
case IA32_MSR_KERNEL_GS_BASE:
*hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE;
return 0;
case IA32_MSR_APIC_BASE:
*hv_reg = HV_X64_REGISTER_APIC_BASE;
return 0;
case IA32_MSR_PAT:
*hv_reg = HV_X64_REGISTER_PAT;
return 0;
case IA32_MSR_SYSENTER_CS:
*hv_reg = HV_X64_REGISTER_SYSENTER_CS;
return 0;
case IA32_MSR_SYSENTER_ESP:
*hv_reg = HV_X64_REGISTER_SYSENTER_ESP;
return 0;
case IA32_MSR_SYSENTER_EIP:
*hv_reg = HV_X64_REGISTER_SYSENTER_EIP;
return 0;
case IA32_MSR_STAR:
*hv_reg = HV_X64_REGISTER_STAR;
return 0;
case IA32_MSR_LSTAR:
*hv_reg = HV_X64_REGISTER_LSTAR;
return 0;
case IA32_MSR_CSTAR:
*hv_reg = HV_X64_REGISTER_CSTAR;
return 0;
case IA32_MSR_SFMASK:
*hv_reg = HV_X64_REGISTER_SFMASK;
return 0;
case IA32_MSR_MTRR_CAP:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP;
return 0;
case IA32_MSR_MTRR_DEF_TYPE:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE;
return 0;
case IA32_MSR_MTRR_PHYSBASE0:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0;
return 0;
case IA32_MSR_MTRR_PHYSMASK0:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0;
return 0;
case IA32_MSR_MTRR_PHYSBASE1:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1;
return 0;
case IA32_MSR_MTRR_PHYSMASK1:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1;
return 0;
case IA32_MSR_MTRR_PHYSBASE2:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2;
return 0;
case IA32_MSR_MTRR_PHYSMASK2:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2;
return 0;
case IA32_MSR_MTRR_PHYSBASE3:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3;
return 0;
case IA32_MSR_MTRR_PHYSMASK3:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3;
return 0;
case IA32_MSR_MTRR_PHYSBASE4:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4;
return 0;
case IA32_MSR_MTRR_PHYSMASK4:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4;
return 0;
case IA32_MSR_MTRR_PHYSBASE5:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5;
return 0;
case IA32_MSR_MTRR_PHYSMASK5:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5;
return 0;
case IA32_MSR_MTRR_PHYSBASE6:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6;
return 0;
case IA32_MSR_MTRR_PHYSMASK6:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6;
return 0;
case IA32_MSR_MTRR_PHYSBASE7:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7;
return 0;
case IA32_MSR_MTRR_PHYSMASK7:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7;
return 0;
case IA32_MSR_MTRR_FIX64K_00000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000;
return 0;
case IA32_MSR_MTRR_FIX16K_80000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000;
return 0;
case IA32_MSR_MTRR_FIX16K_A0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000;
return 0;
case IA32_MSR_MTRR_FIX4K_C0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000;
return 0;
case IA32_MSR_MTRR_FIX4K_C8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000;
return 0;
case IA32_MSR_MTRR_FIX4K_D0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000;
return 0;
case IA32_MSR_MTRR_FIX4K_D8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000;
return 0;
case IA32_MSR_MTRR_FIX4K_E0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000;
return 0;
case IA32_MSR_MTRR_FIX4K_E8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000;
return 0;
case IA32_MSR_MTRR_FIX4K_F0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000;
return 0;
case IA32_MSR_MTRR_FIX4K_F8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000;
return 0;
case IA32_MSR_TSC_AUX:
*hv_reg = HV_X64_REGISTER_TSC_AUX;
return 0;
case IA32_MSR_BNDCFGS:
*hv_reg = HV_X64_REGISTER_BNDCFGS;
return 0;
case IA32_MSR_DEBUG_CTL:
*hv_reg = HV_X64_REGISTER_DEBUG_CTL;
return 0;
case IA32_MSR_TSC_ADJUST:
*hv_reg = HV_X64_REGISTER_TSC_ADJUST;
return 0;
case IA32_MSR_SPEC_CTRL:
*hv_reg = HV_X64_REGISTER_SPEC_CTRL;
return 0;
case HV_X64_MSR_GUEST_OS_ID:
*hv_reg = HV_REGISTER_GUEST_OS_ID;
return 0;
case HV_X64_MSR_SINT0:
*hv_reg = HV_REGISTER_SINT0;
return 0;
case HV_X64_MSR_SINT1:
*hv_reg = HV_REGISTER_SINT1;
return 0;
case HV_X64_MSR_SINT2:
*hv_reg = HV_REGISTER_SINT2;
return 0;
case HV_X64_MSR_SINT3:
*hv_reg = HV_REGISTER_SINT3;
return 0;
case HV_X64_MSR_SINT4:
*hv_reg = HV_REGISTER_SINT4;
return 0;
case HV_X64_MSR_SINT5:
*hv_reg = HV_REGISTER_SINT5;
return 0;
case HV_X64_MSR_SINT6:
*hv_reg = HV_REGISTER_SINT6;
return 0;
case HV_X64_MSR_SINT7:
*hv_reg = HV_REGISTER_SINT7;
return 0;
case HV_X64_MSR_SINT8:
*hv_reg = HV_REGISTER_SINT8;
return 0;
case HV_X64_MSR_SINT9:
*hv_reg = HV_REGISTER_SINT9;
return 0;
case HV_X64_MSR_SINT10:
*hv_reg = HV_REGISTER_SINT10;
return 0;
case HV_X64_MSR_SINT11:
*hv_reg = HV_REGISTER_SINT11;
return 0;
case HV_X64_MSR_SINT12:
*hv_reg = HV_REGISTER_SINT12;
return 0;
case HV_X64_MSR_SINT13:
*hv_reg = HV_REGISTER_SINT13;
return 0;
case HV_X64_MSR_SINT14:
*hv_reg = HV_REGISTER_SINT14;
return 0;
case HV_X64_MSR_SINT15:
*hv_reg = HV_REGISTER_SINT15;
return 0;
case IA32_MSR_MISC_ENABLE:
*hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE;
return 0;
case HV_X64_MSR_SCONTROL:
*hv_reg = HV_REGISTER_SCONTROL;
return 0;
case HV_X64_MSR_SIEFP:
*hv_reg = HV_REGISTER_SIEFP;
return 0;
case HV_X64_MSR_SIMP:
*hv_reg = HV_REGISTER_SIMP;
return 0;
case HV_X64_MSR_REFERENCE_TSC:
*hv_reg = HV_REGISTER_REFERENCE_TSC;
return 0;
case HV_X64_MSR_EOM:
*hv_reg = HV_REGISTER_EOM;
return 0;
default:
error_report("failed to map MSR %u to HV register name", msr);
return -1;
}
}
static int set_msrs(const CPUState *cpu, GList *msrs)
{
size_t n_msrs;
GList *entries;
MshvMsrEntry *entry;
enum hv_register_name name;
struct hv_register_assoc *assoc;
int ret;
size_t i = 0;
n_msrs = g_list_length(msrs);
hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs);
entries = msrs;
for (const GList *elem = entries; elem != NULL; elem = elem->next) {
entry = elem->data;
ret = mshv_msr_to_hv_reg_name(entry->index, &name);
if (ret < 0) {
g_free(assocs);
return ret;
}
assoc = &assocs[i];
assoc->name = name;
/* the union has been initialized to 0 */
assoc->value.reg64 = entry->data;
i++;
}
ret = mshv_set_generic_regs(cpu, assocs, n_msrs);
g_free(assocs);
if (ret < 0) {
error_report("failed to set msrs");
return -1;
}
return 0;
}
int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
size_t n_msrs)
{
GList *valid_msrs = NULL;
uint32_t msr_index;
int ret;
for (size_t i = 0; i < n_msrs; i++) {
msr_index = msrs[i].index;
/* check whether index of msrs is in SUPPORTED_MSRS */
if (mshv_is_supported_msr(msr_index)) {
valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]);
}
}
ret = set_msrs(cpu, valid_msrs);
g_list_free(valid_msrs);
return ret;
}

33
accel/mshv/trace-events Normal file
View file

@ -0,0 +1,33 @@
# Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
# Magnus Kulke <magnuskulke@microsoft.com>
#
# SPDX-License-Identifier: GPL-2.0-or-later
mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s cpu_index=%d"
mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " user=0x%" PRIx64 " readonly=%d result=%d"
mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x"
mshv_mem_ioeventfd_del(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x"
mshv_hvcall_args(const char* hvcall, uint16_t code, uint16_t in_sz) "built args for '%s' code: %d in_sz: %d"
mshv_handle_interrupt(uint32_t cpu, int mask) "cpu_index=%d mask=0x%x"
mshv_set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data) "gsi=%d addr=0x%" PRIx64 " data=0x%x"
mshv_remove_msi_routing(uint32_t gsi) "gsi=%d"
mshv_add_msi_routing(uint64_t addr, uint32_t data) "addr=0x%" PRIx64 " data=0x%x"
mshv_commit_msi_routing_table(int vm_fd, int len) "vm_fd=%d table_size=%d"
mshv_register_irqfd(int vm_fd, int event_fd, uint32_t gsi) "vm_fd=%d event_fd=%d gsi=%d"
mshv_irqchip_update_irqfd_notifier_gsi(int event_fd, int resample_fd, int virq, bool add) "event_fd=%d resample_fd=%d virq=%d add=%d"
mshv_insn_fetch(uint64_t addr, size_t size) "gpa=0x%" PRIx64 " size=%zu"
mshv_mem_write(uint64_t addr, size_t size) "\tgpa=0x%" PRIx64 " size=%zu"
mshv_mem_read(uint64_t addr, size_t size) "\tgpa=0x%" PRIx64 " size=%zu"
mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_set_phys_mem(bool add, const char *name, uint64_t gpa) "\tadd=%d name=%s gpa=0x%010" PRIx64
mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%" PRIx64 " access_type=%d"
mshv_found_slot(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_skip_unset_mem(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_remap_attempt(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_find_slot_by_gpa(uint64_t gpa) "\tgpa=0x%010" PRIx64

14
accel/mshv/trace.h Normal file
View file

@ -0,0 +1,14 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors:
* Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "trace/trace-accel_mshv.h"

View file

@ -5,5 +5,6 @@ system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c')) system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c')) system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
system_stubs_ss.add(when: 'CONFIG_MSHV', if_false: files('mshv-stub.c'))
specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss)

44
accel/stubs/mshv-stub.c Normal file
View file

@ -0,0 +1,44 @@
/*
* QEMU MSHV stub
*
* Copyright Red Hat, Inc. 2025
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/pci/msi.h"
#include "system/mshv.h"
bool mshv_allowed;
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
{
return -ENOSYS;
}
void mshv_irqchip_release_virq(int virq)
{
}
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
{
return -ENOSYS;
}
void mshv_irqchip_commit_routes(void)
{
}
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
const EventNotifier *rn, int virq)
{
return -ENOSYS;
}
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq)
{
return -ENOSYS;
}

View file

@ -17,8 +17,3 @@ G_NORETURN void cpu_loop_exit(CPUState *cpu)
{ {
g_assert_not_reached(); g_assert_not_reached();
} }
G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
{
g_assert_not_reached();
}

View file

@ -122,5 +122,14 @@ GEN_ATOMIC_HELPERS(umax_fetch)
GEN_ATOMIC_HELPERS(xchg) GEN_ATOMIC_HELPERS(xchg)
#if HAVE_CMPXCHG128
ATOMIC_HELPER(xchgo_be, Int128)
ATOMIC_HELPER(xchgo_le, Int128)
ATOMIC_HELPER(fetch_ando_be, Int128)
ATOMIC_HELPER(fetch_ando_le, Int128)
ATOMIC_HELPER(fetch_oro_be, Int128)
ATOMIC_HELPER(fetch_oro_le, Int128)
#endif
#undef ATOMIC_HELPER #undef ATOMIC_HELPER
#undef GEN_ATOMIC_HELPERS #undef GEN_ATOMIC_HELPERS

View file

@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return ret; return ret;
} }
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr) MemOpIdx oi, uintptr_t retaddr)
{ {
@ -108,7 +107,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
DATA_SIZE, retaddr); DATA_SIZE, retaddr);
DATA_TYPE ret; DATA_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, val);
#else
ret = qatomic_xchg__nocheck(haddr, val); ret = qatomic_xchg__nocheck(haddr, val);
#endif
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret), VALUE_LOW(ret),
@ -119,6 +122,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
return ret; return ret;
} }
#if DATA_SIZE == 16
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_and(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return ret;
}
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_or(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return ret;
}
#else
#define GEN_ATOMIC_HELPER(X) \ #define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
@ -188,7 +224,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#undef GEN_ATOMIC_HELPER_FN #undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE < 16 */ #endif /* DATA SIZE == 16 */
#undef END #undef END
@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return BSWAP(ret); return BSWAP(ret);
} }
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr) MemOpIdx oi, uintptr_t retaddr)
{ {
@ -233,7 +268,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
DATA_SIZE, retaddr); DATA_SIZE, retaddr);
ABI_TYPE ret; ABI_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, BSWAP(val));
#else
ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
#endif
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret), VALUE_LOW(ret),
@ -244,6 +283,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
return BSWAP(ret); return BSWAP(ret);
} }
#if DATA_SIZE == 16
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return BSWAP(ret);
}
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return BSWAP(ret);
}
#else
#define GEN_ATOMIC_HELPER(X) \ #define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
@ -317,7 +389,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#undef ADD #undef ADD
#undef GEN_ATOMIC_HELPER_FN #undef GEN_ATOMIC_HELPER_FN
#endif /* DATA_SIZE < 16 */ #endif /* DATA_SIZE == 16 */
#undef END #undef END
#endif /* DATA_SIZE > 1 */ #endif /* DATA_SIZE > 1 */

View file

@ -40,6 +40,7 @@
#include "exec/replay-core.h" #include "exec/replay-core.h"
#include "system/tcg.h" #include "system/tcg.h"
#include "exec/helper-proto-common.h" #include "exec/helper-proto-common.h"
#include "tcg-accel-ops.h"
#include "tb-jmp-cache.h" #include "tb-jmp-cache.h"
#include "tb-hash.h" #include "tb-hash.h"
#include "tb-context.h" #include "tb-context.h"
@ -748,6 +749,20 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
return false; return false;
} }
void tcg_kick_vcpu_thread(CPUState *cpu)
{
/*
* Ensure cpu_exec will see the reason why the exit request was set.
* FIXME: this is not always needed. Other accelerators instead
* read interrupt_request and set exit_request on demand from the
* CPU thread; see kvm_arch_pre_run() for example.
*/
qatomic_store_release(&cpu->exit_request, true);
/* Ensure cpu_exec will see the exit request after TCG has exited. */
qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1);
}
static inline bool icount_exit_request(CPUState *cpu) static inline bool icount_exit_request(CPUState *cpu)
{ {
if (!icount_enabled()) { if (!icount_enabled()) {
@ -774,44 +789,47 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* Clear the interrupt flag now since we're processing /* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request. * cpu->interrupt_request and cpu->exit_request.
* Ensure zeroing happens before reading cpu->exit_request or * Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also smp_wmb in cpu_exit()) * cpu->interrupt_request (see also store-release in
* tcg_kick_vcpu_thread())
*/ */
qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0);
if (unlikely(qatomic_read(&cpu->interrupt_request))) { #ifdef CONFIG_USER_ONLY
int interrupt_request; assert(!cpu_test_interrupt(cpu, ~0));
#else
if (unlikely(cpu_test_interrupt(cpu, ~0))) {
bql_lock(); bql_lock();
interrupt_request = cpu->interrupt_request; if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) {
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG);
/* Mask out external interrupts for this step. */
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
}
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
cpu->exception_index = EXCP_DEBUG; cpu->exception_index = EXCP_DEBUG;
bql_unlock(); bql_unlock();
return true; return true;
} }
#if !defined(CONFIG_USER_ONLY)
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
/* Do nothing */ /* Do nothing */
} else if (interrupt_request & CPU_INTERRUPT_HALT) { } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) {
replay_interrupt(); replay_interrupt();
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
cpu->halted = 1; cpu->halted = 1;
cpu->exception_index = EXCP_HLT; cpu->exception_index = EXCP_HLT;
bql_unlock(); bql_unlock();
return true; return true;
} else { } else {
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
int interrupt_request = cpu->interrupt_request;
if (interrupt_request & CPU_INTERRUPT_RESET) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) {
replay_interrupt(); replay_interrupt();
tcg_ops->cpu_exec_reset(cpu); tcg_ops->cpu_exec_reset(cpu);
bql_unlock(); bql_unlock();
return true; return true;
} }
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
/* Mask out external interrupts for this step. */
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
}
/* /*
* The target hook has 3 exit conditions: * The target hook has 3 exit conditions:
* False when the interrupt isn't processed, * False when the interrupt isn't processed,
@ -836,13 +854,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
cpu->exception_index = -1; cpu->exception_index = -1;
*last_tb = NULL; *last_tb = NULL;
} }
/* The target hook may have updated the 'cpu->interrupt_request';
* reload the 'interrupt_request' value */
interrupt_request = cpu->interrupt_request;
} }
#endif /* !CONFIG_USER_ONLY */ if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) {
if (interrupt_request & CPU_INTERRUPT_EXITTB) { cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB);
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
/* ensure that no TB jump will be modified as /* ensure that no TB jump will be modified as
the program flow was changed */ the program flow was changed */
*last_tb = NULL; *last_tb = NULL;
@ -851,10 +865,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
bql_unlock(); bql_unlock();
} }
#endif /* !CONFIG_USER_ONLY */
/* Finally, check if we need to exit to the main loop. */ /*
if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { * Finally, check if we need to exit to the main loop.
qatomic_set(&cpu->exit_request, 0); * The corresponding store-release is in cpu_exit.
*/
if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) {
if (cpu->exception_index == -1) { if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT; cpu->exception_index = EXCP_INTERRUPT;
} }

View file

@ -25,6 +25,7 @@
#include "accel/tcg/probe.h" #include "accel/tcg/probe.h"
#include "exec/page-protection.h" #include "exec/page-protection.h"
#include "system/memory.h" #include "system/memory.h"
#include "system/physmem.h"
#include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-ldst-common.h"
#include "accel/tcg/cpu-mmu-index.h" #include "accel/tcg/cpu-mmu-index.h"
#include "exec/cputlb.h" #include "exec/cputlb.h"
@ -89,9 +90,6 @@
*/ */
QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data)); QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data));
/* We currently can't handle more than 16 bits in the MMUIDX bitmask.
*/
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1) #define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
static inline size_t tlb_n_entries(CPUTLBDescFast *fast) static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
@ -129,7 +127,7 @@ static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry)
static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx, static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
vaddr addr) vaddr addr)
{ {
uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS; uintptr_t size_mask = cpu_tlb_fast(cpu, mmu_idx)->mask >> CPU_TLB_ENTRY_BITS;
return (addr >> TARGET_PAGE_BITS) & size_mask; return (addr >> TARGET_PAGE_BITS) & size_mask;
} }
@ -138,7 +136,7 @@ static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx, static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx,
vaddr addr) vaddr addr)
{ {
return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; return &cpu_tlb_fast(cpu, mmu_idx)->table[tlb_index(cpu, mmu_idx, addr)];
} }
static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
@ -292,7 +290,7 @@ static void tlb_flush_one_mmuidx_locked(CPUState *cpu, int mmu_idx,
int64_t now) int64_t now)
{ {
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
tlb_mmu_resize_locked(desc, fast, now); tlb_mmu_resize_locked(desc, fast, now);
tlb_mmu_flush_locked(desc, fast); tlb_mmu_flush_locked(desc, fast);
@ -331,7 +329,7 @@ void tlb_init(CPUState *cpu)
cpu->neg.tlb.c.dirty = 0; cpu->neg.tlb.c.dirty = 0;
for (i = 0; i < NB_MMU_MODES; i++) { for (i = 0; i < NB_MMU_MODES; i++) {
tlb_mmu_init(&cpu->neg.tlb.d[i], &cpu->neg.tlb.f[i], now); tlb_mmu_init(&cpu->neg.tlb.d[i], cpu_tlb_fast(cpu, i), now);
} }
} }
@ -342,7 +340,7 @@ void tlb_destroy(CPUState *cpu)
qemu_spin_destroy(&cpu->neg.tlb.c.lock); qemu_spin_destroy(&cpu->neg.tlb.c.lock);
for (i = 0; i < NB_MMU_MODES; i++) { for (i = 0; i < NB_MMU_MODES; i++) {
CPUTLBDesc *desc = &cpu->neg.tlb.d[i]; CPUTLBDesc *desc = &cpu->neg.tlb.d[i];
CPUTLBDescFast *fast = &cpu->neg.tlb.f[i]; CPUTLBDescFast *fast = cpu_tlb_fast(cpu, i);
g_free(fast->table); g_free(fast->table);
g_free(desc->fulltlb); g_free(desc->fulltlb);
@ -370,8 +368,8 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn,
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
{ {
uint16_t asked = data.host_int; MMUIdxMap asked = data.host_int;
uint16_t all_dirty, work, to_clean; MMUIdxMap all_dirty, work, to_clean;
int64_t now = get_clock_realtime(); int64_t now = get_clock_realtime();
assert_cpu_is_self(cpu); assert_cpu_is_self(cpu);
@ -408,7 +406,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
} }
} }
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) void tlb_flush_by_mmuidx(CPUState *cpu, MMUIdxMap idxmap)
{ {
tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap);
@ -422,7 +420,7 @@ void tlb_flush(CPUState *cpu)
tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS); tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS);
} }
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, MMUIdxMap idxmap)
{ {
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
@ -531,7 +529,7 @@ static void tlb_flush_page_locked(CPUState *cpu, int midx, vaddr page)
*/ */
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
vaddr addr, vaddr addr,
uint16_t idxmap) MMUIdxMap idxmap)
{ {
int mmu_idx; int mmu_idx;
@ -570,14 +568,14 @@ static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu,
{ {
vaddr addr_and_idxmap = data.target_ptr; vaddr addr_and_idxmap = data.target_ptr;
vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK; vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK;
uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK; MMUIdxMap idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
} }
typedef struct { typedef struct {
vaddr addr; vaddr addr;
uint16_t idxmap; MMUIdxMap idxmap;
} TLBFlushPageByMMUIdxData; } TLBFlushPageByMMUIdxData;
/** /**
@ -599,7 +597,7 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu,
g_free(d); g_free(d);
} }
void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap) void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, MMUIdxMap idxmap)
{ {
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap); tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap);
@ -618,7 +616,7 @@ void tlb_flush_page(CPUState *cpu, vaddr addr)
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr, vaddr addr,
uint16_t idxmap) MMUIdxMap idxmap)
{ {
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
@ -667,7 +665,7 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
unsigned bits) unsigned bits)
{ {
CPUTLBDesc *d = &cpu->neg.tlb.d[midx]; CPUTLBDesc *d = &cpu->neg.tlb.d[midx];
CPUTLBDescFast *f = &cpu->neg.tlb.f[midx]; CPUTLBDescFast *f = cpu_tlb_fast(cpu, midx);
vaddr mask = MAKE_64BIT_MASK(0, bits); vaddr mask = MAKE_64BIT_MASK(0, bits);
/* /*
@ -715,8 +713,8 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
typedef struct { typedef struct {
vaddr addr; vaddr addr;
vaddr len; vaddr len;
uint16_t idxmap; MMUIdxMap idxmap;
uint16_t bits; unsigned bits;
} TLBFlushRangeData; } TLBFlushRangeData;
static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu, static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
@ -766,7 +764,7 @@ static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu,
} }
void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
vaddr len, uint16_t idxmap, vaddr len, MMUIdxMap idxmap,
unsigned bits) unsigned bits)
{ {
TLBFlushRangeData d; TLBFlushRangeData d;
@ -797,7 +795,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
} }
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
uint16_t idxmap, unsigned bits) MMUIdxMap idxmap, unsigned bits)
{ {
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
} }
@ -805,7 +803,7 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr, vaddr addr,
vaddr len, vaddr len,
uint16_t idxmap, MMUIdxMap idxmap,
unsigned bits) unsigned bits)
{ {
TLBFlushRangeData d, *p; TLBFlushRangeData d, *p;
@ -847,7 +845,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr, vaddr addr,
uint16_t idxmap, MMUIdxMap idxmap,
unsigned bits) unsigned bits)
{ {
tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE, tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE,
@ -858,7 +856,7 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
can be detected */ can be detected */
void tlb_protect_code(ram_addr_t ram_addr) void tlb_protect_code(ram_addr_t ram_addr)
{ {
cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK, physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
TARGET_PAGE_SIZE, TARGET_PAGE_SIZE,
DIRTY_MEMORY_CODE); DIRTY_MEMORY_CODE);
} }
@ -867,7 +865,7 @@ void tlb_protect_code(ram_addr_t ram_addr)
tested for self modifying code */ tested for self modifying code */
void tlb_unprotect_code(ram_addr_t ram_addr) void tlb_unprotect_code(ram_addr_t ram_addr)
{ {
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
} }
@ -923,7 +921,7 @@ void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length)
qemu_spin_lock(&cpu->neg.tlb.c.lock); qemu_spin_lock(&cpu->neg.tlb.c.lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
unsigned int n = tlb_n_entries(fast); unsigned int n = tlb_n_entries(fast);
unsigned int i; unsigned int i;
@ -1085,7 +1083,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
if (prot & PAGE_WRITE) { if (prot & PAGE_WRITE) {
if (section->readonly) { if (section->readonly) {
write_flags |= TLB_DISCARD_WRITE; write_flags |= TLB_DISCARD_WRITE;
} else if (cpu_physical_memory_is_clean(iotlb)) { } else if (physical_memory_is_clean(iotlb)) {
write_flags |= TLB_NOTDIRTY; write_flags |= TLB_NOTDIRTY;
} }
} }
@ -1316,7 +1314,7 @@ static bool victim_tlb_hit(CPUState *cpu, size_t mmu_idx, size_t index,
if (cmp == page) { if (cmp == page) {
/* Found entry in victim tlb, swap tlb and iotlb. */ /* Found entry in victim tlb, swap tlb and iotlb. */
CPUTLBEntry tmptlb, *tlb = &cpu->neg.tlb.f[mmu_idx].table[index]; CPUTLBEntry tmptlb, *tlb = &cpu_tlb_fast(cpu, mmu_idx)->table[index];
qemu_spin_lock(&cpu->neg.tlb.c.lock); qemu_spin_lock(&cpu->neg.tlb.c.lock);
copy_tlb_helper_locked(&tmptlb, tlb); copy_tlb_helper_locked(&tmptlb, tlb);
@ -1341,7 +1339,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size); trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { if (!physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr); tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr);
} }
@ -1349,10 +1347,10 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
* Set both VGA and migration bits for simplicity and to remove * Set both VGA and migration bits for simplicity and to remove
* the notdirty callback faster. * the notdirty callback faster.
*/ */
cpu_physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE); physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE);
/* We remove the notdirty callback only if the code has been flushed. */ /* We remove the notdirty callback only if the code has been flushed. */
if (!cpu_physical_memory_is_clean(ram_addr)) { if (!physical_memory_is_clean(ram_addr)) {
trace_memory_notdirty_set_dirty(mem_vaddr); trace_memory_notdirty_set_dirty(mem_vaddr);
tlb_set_dirty(cpu, mem_vaddr); tlb_set_dirty(cpu, mem_vaddr);
} }
@ -1670,18 +1668,7 @@ static bool mmu_lookup1(CPUState *cpu, MMULookupPageData *data, MemOp memop,
if (likely(!maybe_resized)) { if (likely(!maybe_resized)) {
/* Alignment has not been checked by tlb_fill_align. */ /* Alignment has not been checked by tlb_fill_align. */
int a_bits = memop_alignment_bits(memop); int a_bits = memop_tlb_alignment_bits(memop, flags & TLB_CHECK_ALIGNED);
/*
* This alignment check differs from the one above, in that this is
* based on the atomicity of the operation. The intended use case is
* the ARM memory type field of each PTE, where access to pages with
* Device memory type require alignment.
*/
if (unlikely(flags & TLB_CHECK_ALIGNED)) {
int at_bits = memop_atomicity_bits(memop);
a_bits = MAX(a_bits, at_bits);
}
if (unlikely(addr & ((1 << a_bits) - 1))) { if (unlikely(addr & ((1 << a_bits) - 1))) {
cpu_unaligned_access(cpu, addr, access_type, mmu_idx, ra); cpu_unaligned_access(cpu, addr, access_type, mmu_idx, ra);
} }
@ -1744,6 +1731,7 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType type, MMULookupLocals *l) uintptr_t ra, MMUAccessType type, MMULookupLocals *l)
{ {
bool crosspage; bool crosspage;
vaddr last;
int flags; int flags;
l->memop = get_memop(oi); l->memop = get_memop(oi);
@ -1753,13 +1741,15 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
l->page[0].addr = addr; l->page[0].addr = addr;
l->page[0].size = memop_size(l->memop); l->page[0].size = memop_size(l->memop);
l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK; l->page[1].addr = 0;
l->page[1].size = 0; l->page[1].size = 0;
crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK;
if (likely(!crosspage)) { /* Lookup and recognize exceptions from the first page. */
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra); mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
last = addr + l->page[0].size - 1;
crosspage = (addr ^ last) & TARGET_PAGE_MASK;
if (likely(!crosspage)) {
flags = l->page[0].flags; flags = l->page[0].flags;
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) { if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
mmu_watch_or_dirty(cpu, &l->page[0], type, ra); mmu_watch_or_dirty(cpu, &l->page[0], type, ra);
@ -1769,18 +1759,18 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
} }
} else { } else {
/* Finish compute of page crossing. */ /* Finish compute of page crossing. */
int size0 = l->page[1].addr - addr; vaddr addr1 = last & TARGET_PAGE_MASK;
int size0 = addr1 - addr;
l->page[1].size = l->page[0].size - size0; l->page[1].size = l->page[0].size - size0;
l->page[0].size = size0; l->page[0].size = size0;
l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx, l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx,
l->page[1].addr, addr); addr1, addr);
/* /*
* Lookup both pages, recognizing exceptions from either. If the * Lookup and recognize exceptions from the second page.
* second lookup potentially resized, refresh first CPUTLBEntryFull. * If the lookup potentially resized the table, refresh the
* first CPUTLBEntryFull pointer.
*/ */
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
if (mmu_lookup1(cpu, &l->page[1], 0, l->mmu_idx, type, ra)) { if (mmu_lookup1(cpu, &l->page[1], 0, l->mmu_idx, type, ra)) {
uintptr_t index = tlb_index(cpu, l->mmu_idx, addr); uintptr_t index = tlb_index(cpu, l->mmu_idx, addr);
l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index]; l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index];

View file

@ -102,8 +102,8 @@ static TCGv_i32 gen_cpu_index(void)
/* /*
* Optimize when we run with a single vcpu. All values using cpu_index, * Optimize when we run with a single vcpu. All values using cpu_index,
* including scoreboard index, will be optimized out. * including scoreboard index, will be optimized out.
* User-mode calls tb_flush when setting this flag. In system-mode, all * User-mode flushes all TBs when setting this flag.
* vcpus are created before generating code. * In system-mode, all vcpus are created before generating code.
*/ */
if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) { if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) {
return tcg_constant_i32(current_cpu->cpu_index); return tcg_constant_i32(current_cpu->cpu_index);

View file

@ -36,8 +36,11 @@
#include "internal-common.h" #include "internal-common.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#include "user/page-protection.h" #include "user/page-protection.h"
#define runstate_is_running() true
#else
#include "system/runstate.h"
#endif #endif
#include "trace.h"
/* List iterators for lists of tagged pointers in TranslationBlock. */ /* List iterators for lists of tagged pointers in TranslationBlock. */
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \ #define TB_FOR_EACH_TAGGED(head, tb, n, field) \
@ -88,7 +91,10 @@ static IntervalTreeRoot tb_root;
static void tb_remove_all(void) static void tb_remove_all(void)
{ {
assert_memory_lock(); /*
* Only called from tb_flush__exclusive_or_serial, where we have already
* asserted that we're in an exclusive state.
*/
memset(&tb_root, 0, sizeof(tb_root)); memset(&tb_root, 0, sizeof(tb_root));
} }
@ -756,17 +762,20 @@ static void tb_remove(TranslationBlock *tb)
} }
#endif /* CONFIG_USER_ONLY */ #endif /* CONFIG_USER_ONLY */
/* flush all the translation blocks */ /*
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) * Flush all the translation blocks.
* Must be called from a context in which no cpus are running,
* e.g. start_exclusive() or vm_stop().
*/
void tb_flush__exclusive_or_serial(void)
{ {
bool did_flush = false; CPUState *cpu;
mmap_lock(); trace_tb_flush();
/* If it is already been done on request of another CPU, just retry. */ assert(tcg_enabled());
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) { /* Note that cpu_in_serial_context checks cpu_in_exclusive_context. */
goto done; assert(!runstate_is_running() ||
} (current_cpu && cpu_in_serial_context(current_cpu)));
did_flush = true;
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
tcg_flush_jmp_cache(cpu); tcg_flush_jmp_cache(cpu);
@ -778,27 +787,25 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
tcg_region_reset_all(); tcg_region_reset_all();
/* XXX: flush processor icache at this point if cache flush is expensive */ /* XXX: flush processor icache at this point if cache flush is expensive */
qatomic_inc(&tb_ctx.tb_flush_count); qatomic_inc(&tb_ctx.tb_flush_count);
done:
mmap_unlock();
if (did_flush) {
qemu_plugin_flush_cb(); qemu_plugin_flush_cb();
} }
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{
/* If it is already been done on request of another CPU, just retry. */
if (tb_ctx.tb_flush_count == tb_flush_count.host_int) {
tb_flush__exclusive_or_serial();
}
} }
void tb_flush(CPUState *cpu) void queue_tb_flush(CPUState *cs)
{ {
if (tcg_enabled()) { if (tcg_enabled()) {
unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count); unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count);
async_safe_run_on_cpu(cs, do_tb_flush,
if (cpu_in_serial_context(cpu)) {
do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
} else {
async_safe_run_on_cpu(cpu, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count)); RUN_ON_CPU_HOST_INT(tb_flush_count));
} }
} }
}
/* remove @orig from its @n_orig-th jump list */ /* remove @orig from its @n_orig-th jump list */
static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
@ -836,6 +843,14 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
* We first acquired the lock, and since the destination pointer matches, * We first acquired the lock, and since the destination pointer matches,
* we know for sure that @orig is in the jmp list. * we know for sure that @orig is in the jmp list.
*/ */
if (dest == orig) {
/*
* In the case of a TB that links to itself, removing the entry
* from the list means that it won't be present later during
* tb_jmp_unlink -- unlink now.
*/
tb_reset_jump(orig, n_orig);
}
pprev = &dest->jmp_list_head; pprev = &dest->jmp_list_head;
TB_FOR_EACH_JMP(dest, tb, n) { TB_FOR_EACH_JMP(dest, tb, n) {
if (tb == orig && n == n_orig) { if (tb == orig && n == n_orig) {
@ -1154,7 +1169,6 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
page_collection_unlock(pages); page_collection_unlock(pages);
/* Force execution of one insn next time. */ /* Force execution of one insn next time. */
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
mmap_unlock();
cpu_loop_exit_noexc(cpu); cpu_loop_exit_noexc(cpu);
} }
} }

View file

@ -84,10 +84,9 @@ static void *mttcg_cpu_thread_fn(void *arg)
cpu_thread_signal_created(cpu); cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed); qemu_guest_random_seed_thread_part2(cpu->random_seed);
/* process any pending work */
cpu->exit_request = 1;
do { do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) { if (cpu_can_run(cpu)) {
int r; int r;
bql_unlock(); bql_unlock();
@ -112,8 +111,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
break; break;
} }
} }
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu)); } while (!cpu->unplug || cpu_can_run(cpu));
tcg_cpu_destroy(cpu); tcg_cpu_destroy(cpu);
@ -123,11 +120,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
return NULL; return NULL;
} }
void mttcg_kick_vcpu_thread(CPUState *cpu)
{
cpu_exit(cpu);
}
void mttcg_start_vcpu_thread(CPUState *cpu) void mttcg_start_vcpu_thread(CPUState *cpu)
{ {
char thread_name[VCPU_THREAD_NAME_SIZE]; char thread_name[VCPU_THREAD_NAME_SIZE];

View file

@ -10,9 +10,6 @@
#ifndef TCG_ACCEL_OPS_MTTCG_H #ifndef TCG_ACCEL_OPS_MTTCG_H
#define TCG_ACCEL_OPS_MTTCG_H #define TCG_ACCEL_OPS_MTTCG_H
/* kick MTTCG vCPU thread */
void mttcg_kick_vcpu_thread(CPUState *cpu);
/* start an mttcg vCPU thread */ /* start an mttcg vCPU thread */
void mttcg_start_vcpu_thread(CPUState *cpu); void mttcg_start_vcpu_thread(CPUState *cpu);

View file

@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused)
CPUState *cpu; CPUState *cpu;
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
cpu_exit(cpu); tcg_kick_vcpu_thread(cpu);
}; };
} }
@ -117,7 +117,7 @@ static void rr_wait_io_event(void)
rr_start_kick_timer(); rr_start_kick_timer();
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
qemu_wait_io_event_common(cpu); qemu_process_cpu_events_common(cpu);
} }
} }
@ -197,13 +197,13 @@ static void *rr_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed); qemu_guest_random_seed_thread_part2(cpu->random_seed);
/* wait for initial kick-off after machine start */ /* wait for initial kick-off after machine start */
while (first_cpu->stopped) { while (cpu_is_stopped(first_cpu)) {
qemu_cond_wait_bql(first_cpu->halt_cond); qemu_cond_wait_bql(first_cpu->halt_cond);
/* process any pending work */ /* process any pending work */
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
current_cpu = cpu; current_cpu = cpu;
qemu_wait_io_event_common(cpu); qemu_process_cpu_events_common(cpu);
} }
} }
@ -211,13 +211,30 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu; cpu = first_cpu;
/* process any pending work */
cpu->exit_request = 1;
while (1) { while (1) {
/* Only used for icount_enabled() */ /* Only used for icount_enabled() */
int64_t cpu_budget = 0; int64_t cpu_budget = 0;
if (cpu) {
/*
* This could even reset exit_request for all CPUs, but in practice
* races between CPU exits and changes to "cpu" are so rare that
* there's no advantage in doing so.
*/
qatomic_set(&cpu->exit_request, false);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
rr_wait_io_event();
rr_deal_with_unplugged_cpus();
bql_unlock(); bql_unlock();
replay_mutex_lock(); replay_mutex_lock();
bql_lock(); bql_lock();
@ -242,10 +259,17 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu; cpu = first_cpu;
} }
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { while (cpu && cpu_work_list_empty(cpu)) {
/* Store rr_current_cpu before evaluating cpu_can_run(). */ /*
* Store rr_current_cpu before evaluating cpu->exit_request.
* Pairs with rr_kick_next_cpu().
*/
qatomic_set_mb(&rr_current_cpu, cpu); qatomic_set_mb(&rr_current_cpu, cpu);
/* Pairs with store-release in cpu_exit. */
if (qatomic_load_acquire(&cpu->exit_request)) {
break;
}
current_cpu = cpu; current_cpu = cpu;
qemu_clock_enable(QEMU_CLOCK_VIRTUAL, qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
@ -285,21 +309,6 @@ static void *rr_cpu_thread_fn(void *arg)
/* Does not need a memory barrier because a spurious wakeup is okay. */ /* Does not need a memory barrier because a spurious wakeup is okay. */
qatomic_set(&rr_current_cpu, NULL); qatomic_set(&rr_current_cpu, NULL);
if (cpu && cpu->exit_request) {
qatomic_set_mb(&cpu->exit_request, 0);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
rr_wait_io_event();
rr_deal_with_unplugged_cpus();
} }
g_assert_not_reached(); g_assert_not_reached();

View file

@ -82,8 +82,6 @@ int tcg_cpu_exec(CPUState *cpu)
ret = cpu_exec(cpu); ret = cpu_exec(cpu);
cpu_exec_end(cpu); cpu_exec_end(cpu);
qatomic_set_mb(&cpu->exit_request, 0);
return ret; return ret;
} }
@ -97,7 +95,7 @@ static void tcg_cpu_reset_hold(CPUState *cpu)
/* mask must never be zero, except for A20 change call */ /* mask must never be zero, except for A20 change call */
void tcg_handle_interrupt(CPUState *cpu, int mask) void tcg_handle_interrupt(CPUState *cpu, int mask)
{ {
cpu->interrupt_request |= mask; cpu_set_interrupt(cpu, mask);
/* /*
* If called from iothread context, wake the target cpu in * If called from iothread context, wake the target cpu in
@ -206,7 +204,7 @@ static void tcg_accel_ops_init(AccelClass *ac)
if (qemu_tcg_mttcg_enabled()) { if (qemu_tcg_mttcg_enabled()) {
ops->create_vcpu_thread = mttcg_start_vcpu_thread; ops->create_vcpu_thread = mttcg_start_vcpu_thread;
ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; ops->kick_vcpu_thread = tcg_kick_vcpu_thread;
ops->handle_interrupt = tcg_handle_interrupt; ops->handle_interrupt = tcg_handle_interrupt;
} else { } else {
ops->create_vcpu_thread = rr_start_vcpu_thread; ops->create_vcpu_thread = rr_start_vcpu_thread;

View file

@ -18,5 +18,6 @@ void tcg_cpu_destroy(CPUState *cpu);
int tcg_cpu_exec(CPUState *cpu); int tcg_cpu_exec(CPUState *cpu);
void tcg_handle_interrupt(CPUState *cpu, int mask); void tcg_handle_interrupt(CPUState *cpu, int mask);
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel); void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
void tcg_kick_vcpu_thread(CPUState *cpu);
#endif /* TCG_ACCEL_OPS_H */ #endif /* TCG_ACCEL_OPS_H */

View file

@ -38,6 +38,8 @@
#include "qemu/target-info.h" #include "qemu/target-info.h"
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
#include "hw/boards.h" #include "hw/boards.h"
#include "exec/tb-flush.h"
#include "system/runstate.h"
#endif #endif
#include "accel/accel-ops.h" #include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h" #include "accel/accel-cpu-ops.h"
@ -82,6 +84,23 @@ static void tcg_accel_instance_init(Object *obj)
bool one_insn_per_tb; bool one_insn_per_tb;
#ifndef CONFIG_USER_ONLY
static void tcg_vm_change_state(void *opaque, bool running, RunState state)
{
if (state == RUN_STATE_RESTORE_VM) {
/*
* loadvm will update the content of RAM, bypassing the usual
* mechanisms that ensure we flush TBs for writes to memory
* we've translated code from, so we must flush all TBs.
*
* vm_stop() has just stopped all cpus, so we are exclusive.
*/
assert(!running);
tb_flush__exclusive_or_serial();
}
}
#endif
static int tcg_init_machine(AccelState *as, MachineState *ms) static int tcg_init_machine(AccelState *as, MachineState *ms)
{ {
TCGState *s = TCG_STATE(as); TCGState *s = TCG_STATE(as);
@ -124,6 +143,8 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
default: default:
g_assert_not_reached(); g_assert_not_reached();
} }
qemu_add_vm_change_state_handler(tcg_vm_change_state, NULL);
#endif #endif
tcg_allowed = true; tcg_allowed = true;

View file

@ -63,6 +63,18 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i128, i32) i128, env, i64, i128, i128, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG, DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i128, i32) i128, env, i64, i128, i128, i32)
DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
#endif #endif
DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG, DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG,

View file

@ -12,6 +12,7 @@ memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64
# translate-all.c # translate-all.c
translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
tb_gen_code_buffer_overflow(const char *reason) "reason: %s"
# ldst_atomicity # ldst_atomicity
load_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" load_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
@ -24,3 +25,6 @@ store_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIx
store_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" store_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
store_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" store_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
store_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" store_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
# tb-maint.c
tb_flush(void) ""

View file

@ -289,7 +289,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
tb = tcg_tb_alloc(tcg_ctx); tb = tcg_tb_alloc(tcg_ctx);
if (unlikely(!tb)) { if (unlikely(!tb)) {
/* flush must be done */ /* flush must be done */
tb_flush(cpu); if (cpu_in_serial_context(cpu)) {
trace_tb_gen_code_buffer_overflow("tcg_tb_alloc");
tb_flush__exclusive_or_serial();
goto buffer_overflow;
}
queue_tb_flush(cpu);
mmap_unlock(); mmap_unlock();
/* Make the execution loop process the flush as soon as possible. */ /* Make the execution loop process the flush as soon as possible. */
cpu->exception_index = EXCP_INTERRUPT; cpu->exception_index = EXCP_INTERRUPT;
@ -321,6 +326,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
if (unlikely(gen_code_size < 0)) { if (unlikely(gen_code_size < 0)) {
switch (gen_code_size) { switch (gen_code_size) {
case -1: case -1:
trace_tb_gen_code_buffer_overflow("setjmp_gen_code");
/* /*
* Overflow of code_gen_buffer, or the current slice of it. * Overflow of code_gen_buffer, or the current slice of it.
* *
@ -385,6 +391,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
if (unlikely(search_size < 0)) { if (unlikely(search_size < 0)) {
trace_tb_gen_code_buffer_overflow("encode_search");
tb_unlock_pages(tb); tb_unlock_pages(tb);
goto buffer_overflow; goto buffer_overflow;
} }

View file

@ -38,6 +38,7 @@
#include "qemu/int128.h" #include "qemu/int128.h"
#include "trace.h" #include "trace.h"
#include "tcg/tcg-ldst.h" #include "tcg/tcg-ldst.h"
#include "tcg-accel-ops.h"
#include "backend-ldst.h" #include "backend-ldst.h"
#include "internal-common.h" #include "internal-common.h"
#include "tb-internal.h" #include "tb-internal.h"
@ -46,11 +47,15 @@ __thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL //#define DEBUG_SIGNAL
void cpu_interrupt(CPUState *cpu, int mask) void qemu_cpu_kick(CPUState *cpu)
{ {
g_assert(bql_locked()); tcg_kick_vcpu_thread(cpu);
cpu->interrupt_request |= mask; }
qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
void qemu_process_cpu_events(CPUState *cpu)
{
qatomic_set(&cpu->exit_request, false);
process_queued_cpu_work(cpu);
} }
/* /*
@ -264,48 +269,6 @@ static void pageflags_create(vaddr start, vaddr last, int flags)
interval_tree_insert(&p->itree, &pageflags_root); interval_tree_insert(&p->itree, &pageflags_root);
} }
/* A subroutine of page_set_flags: remove everything in [start,last]. */
static bool pageflags_unset(vaddr start, vaddr last)
{
bool inval_tb = false;
while (true) {
PageFlagsNode *p = pageflags_find(start, last);
vaddr p_last;
if (!p) {
break;
}
if (p->flags & PAGE_EXEC) {
inval_tb = true;
}
interval_tree_remove(&p->itree, &pageflags_root);
p_last = p->itree.last;
if (p->itree.start < start) {
/* Truncate the node from the end, or split out the middle. */
p->itree.last = start - 1;
interval_tree_insert(&p->itree, &pageflags_root);
if (last < p_last) {
pageflags_create(last + 1, p_last, p->flags);
break;
}
} else if (p_last <= last) {
/* Range completely covers node -- remove it. */
g_free_rcu(p, rcu);
} else {
/* Truncate the node from the start. */
p->itree.start = last + 1;
interval_tree_insert(&p->itree, &pageflags_root);
break;
}
}
return inval_tb;
}
/* /*
* A subroutine of page_set_flags: nothing overlaps [start,last], * A subroutine of page_set_flags: nothing overlaps [start,last],
* but check adjacent mappings and maybe merge into a single range. * but check adjacent mappings and maybe merge into a single range.
@ -351,15 +314,6 @@ static void pageflags_create_merge(vaddr start, vaddr last, int flags)
} }
} }
/*
* Allow the target to decide if PAGE_TARGET_[12] may be reset.
* By default, they are not kept.
*/
#ifndef PAGE_TARGET_STICKY
#define PAGE_TARGET_STICKY 0
#endif
#define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY)
/* A subroutine of page_set_flags: add flags to [start,last]. */ /* A subroutine of page_set_flags: add flags to [start,last]. */
static bool pageflags_set_clear(vaddr start, vaddr last, static bool pageflags_set_clear(vaddr start, vaddr last,
int set_flags, int clear_flags) int set_flags, int clear_flags)
@ -372,7 +326,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
restart: restart:
p = pageflags_find(start, last); p = pageflags_find(start, last);
if (!p) { if (!p) {
if (set_flags) { if (set_flags & PAGE_VALID) {
pageflags_create_merge(start, last, set_flags); pageflags_create_merge(start, last, set_flags);
} }
goto done; goto done;
@ -386,11 +340,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
/* /*
* Need to flush if an overlapping executable region * Need to flush if an overlapping executable region
* removes exec, or adds write. * removes exec, adds write, or is a new mapping.
*/ */
if ((p_flags & PAGE_EXEC) if ((p_flags & PAGE_EXEC)
&& (!(merge_flags & PAGE_EXEC) && (!(merge_flags & PAGE_EXEC)
|| (merge_flags & ~p_flags & PAGE_WRITE))) { || (merge_flags & ~p_flags & PAGE_WRITE)
|| (clear_flags & PAGE_VALID))) {
inval_tb = true; inval_tb = true;
} }
@ -399,7 +354,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
* attempting to merge with adjacent regions. * attempting to merge with adjacent regions.
*/ */
if (start == p_start && last == p_last) { if (start == p_start && last == p_last) {
if (merge_flags) { if (merge_flags & PAGE_VALID) {
p->flags = merge_flags; p->flags = merge_flags;
} else { } else {
interval_tree_remove(&p->itree, &pageflags_root); interval_tree_remove(&p->itree, &pageflags_root);
@ -419,12 +374,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
interval_tree_insert(&p->itree, &pageflags_root); interval_tree_insert(&p->itree, &pageflags_root);
if (last < p_last) { if (last < p_last) {
if (merge_flags) { if (merge_flags & PAGE_VALID) {
pageflags_create(start, last, merge_flags); pageflags_create(start, last, merge_flags);
} }
pageflags_create(last + 1, p_last, p_flags); pageflags_create(last + 1, p_last, p_flags);
} else { } else {
if (merge_flags) { if (merge_flags & PAGE_VALID) {
pageflags_create(start, p_last, merge_flags); pageflags_create(start, p_last, merge_flags);
} }
if (p_last < last) { if (p_last < last) {
@ -433,18 +388,18 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
} }
} }
} else { } else {
if (start < p_start && set_flags) { if (start < p_start && (set_flags & PAGE_VALID)) {
pageflags_create(start, p_start - 1, set_flags); pageflags_create(start, p_start - 1, set_flags);
} }
if (last < p_last) { if (last < p_last) {
interval_tree_remove(&p->itree, &pageflags_root); interval_tree_remove(&p->itree, &pageflags_root);
p->itree.start = last + 1; p->itree.start = last + 1;
interval_tree_insert(&p->itree, &pageflags_root); interval_tree_insert(&p->itree, &pageflags_root);
if (merge_flags) { if (merge_flags & PAGE_VALID) {
pageflags_create(start, last, merge_flags); pageflags_create(start, last, merge_flags);
} }
} else { } else {
if (merge_flags) { if (merge_flags & PAGE_VALID) {
p->flags = merge_flags; p->flags = merge_flags;
} else { } else {
interval_tree_remove(&p->itree, &pageflags_root); interval_tree_remove(&p->itree, &pageflags_root);
@ -492,7 +447,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
g_free_rcu(p, rcu); g_free_rcu(p, rcu);
goto restart; goto restart;
} }
if (set_flags) { if (set_flags & PAGE_VALID) {
pageflags_create(start, last, set_flags); pageflags_create(start, last, set_flags);
} }
@ -500,42 +455,36 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
return inval_tb; return inval_tb;
} }
void page_set_flags(vaddr start, vaddr last, int flags) void page_set_flags(vaddr start, vaddr last, int set_flags, int clear_flags)
{ {
bool reset = false; /*
bool inval_tb = false; * This function should never be called with addresses outside the
* guest address space. If this assert fires, it probably indicates
/* This function should never be called with addresses outside the * a missing call to h2g_valid.
guest address space. If this assert fires, it probably indicates */
a missing call to h2g_valid. */
assert(start <= last); assert(start <= last);
assert(last <= guest_addr_max); assert(last <= guest_addr_max);
/* Only set PAGE_ANON with new mappings. */
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
assert_memory_lock(); assert_memory_lock();
start &= TARGET_PAGE_MASK; start &= TARGET_PAGE_MASK;
last |= ~TARGET_PAGE_MASK; last |= ~TARGET_PAGE_MASK;
if (!(flags & PAGE_VALID)) { if (set_flags & PAGE_WRITE) {
flags = 0; set_flags |= PAGE_WRITE_ORG;
} else {
reset = flags & PAGE_RESET;
flags &= ~PAGE_RESET;
if (flags & PAGE_WRITE) {
flags |= PAGE_WRITE_ORG;
} }
if (clear_flags & PAGE_WRITE) {
clear_flags |= PAGE_WRITE_ORG;
} }
if (!flags || reset) { if (clear_flags & PAGE_VALID) {
page_reset_target_data(start, last); page_reset_target_data(start, last);
inval_tb |= pageflags_unset(start, last); clear_flags = -1;
} else {
/* Only set PAGE_ANON with new mappings. */
assert(!(set_flags & PAGE_ANON));
} }
if (flags) {
inval_tb |= pageflags_set_clear(start, last, flags, if (pageflags_set_clear(start, last, set_flags, clear_flags)) {
~(reset ? 0 : PAGE_STICKY));
}
if (inval_tb) {
tb_invalidate_phys_range(NULL, start, last); tb_invalidate_phys_range(NULL, start, last);
} }
} }

View file

@ -26,7 +26,7 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "audio.h" #include "qemu/audio.h"
#include "trace.h" #include "trace.h"
#pragma GCC diagnostic ignored "-Waddress" #pragma GCC diagnostic ignored "-Waddress"
@ -41,7 +41,7 @@ struct pollhlp {
struct pollfd *pfds; struct pollfd *pfds;
int count; int count;
int mask; int mask;
AudioState *s; AudioBackend *s;
}; };
typedef struct ALSAVoiceOut { typedef struct ALSAVoiceOut {
@ -264,7 +264,7 @@ static int alsa_poll_in (HWVoiceIn *hw)
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN); return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
} }
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) static snd_pcm_format_t aud_to_alsafmt(AudioFormat fmt, bool big_endian)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUDIO_FORMAT_S8:
@ -274,39 +274,19 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_U8; return SND_PCM_FORMAT_U8;
case AUDIO_FORMAT_S16: case AUDIO_FORMAT_S16:
if (endianness) { return big_endian ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE;
return SND_PCM_FORMAT_S16_BE;
} else {
return SND_PCM_FORMAT_S16_LE;
}
case AUDIO_FORMAT_U16: case AUDIO_FORMAT_U16:
if (endianness) { return big_endian ? SND_PCM_FORMAT_U16_BE : SND_PCM_FORMAT_U16_LE;
return SND_PCM_FORMAT_U16_BE;
} else {
return SND_PCM_FORMAT_U16_LE;
}
case AUDIO_FORMAT_S32: case AUDIO_FORMAT_S32:
if (endianness) { return big_endian ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE;
return SND_PCM_FORMAT_S32_BE;
} else {
return SND_PCM_FORMAT_S32_LE;
}
case AUDIO_FORMAT_U32: case AUDIO_FORMAT_U32:
if (endianness) { return big_endian ? SND_PCM_FORMAT_U32_BE : SND_PCM_FORMAT_U32_LE;
return SND_PCM_FORMAT_U32_BE;
} else {
return SND_PCM_FORMAT_U32_LE;
}
case AUDIO_FORMAT_F32: case AUDIO_FORMAT_F32:
if (endianness) { return big_endian ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE;
return SND_PCM_FORMAT_FLOAT_BE;
} else {
return SND_PCM_FORMAT_FLOAT_LE;
}
default: default:
dolog ("Internal logic error: Bad audio format %d\n", fmt); dolog ("Internal logic error: Bad audio format %d\n", fmt);
@ -956,7 +936,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
static struct audio_driver alsa_audio_driver = { static struct audio_driver alsa_audio_driver = {
.name = "alsa", .name = "alsa",
.descr = "ALSA http://www.alsa-project.org",
.init = alsa_audio_init, .init = alsa_audio_init,
.fini = alsa_audio_fini, .fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops, .pcm_ops = &alsa_pcm_ops,

View file

@ -23,11 +23,12 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "audio/audio.h" #include "audio_int.h"
#include "monitor/hmp.h" #include "monitor/hmp.h"
#include "monitor/monitor.h" #include "monitor/monitor.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qobject/qdict.h" #include "qobject/qdict.h"
#include "qemu/error-report.h"
static QLIST_HEAD (capture_list_head, CaptureState) capture_head; static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
@ -36,6 +37,8 @@ void hmp_info_capture(Monitor *mon, const QDict *qdict)
int i; int i;
CaptureState *s; CaptureState *s;
warn_report_once("'info capture' is deprecated since v10.2, to be removed");
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
monitor_printf(mon, "[%d]: ", i); monitor_printf(mon, "[%d]: ", i);
s->ops.info (s->opaque); s->ops.info (s->opaque);
@ -48,6 +51,8 @@ void hmp_stopcapture(Monitor *mon, const QDict *qdict)
int n = qdict_get_int(qdict, "n"); int n = qdict_get_int(qdict, "n");
CaptureState *s; CaptureState *s;
warn_report_once("'stopcapture' is deprecated since v10.2, to be removed");
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
if (i == n) { if (i == n) {
s->ops.destroy (s->opaque); s->ops.destroy (s->opaque);
@ -67,7 +72,9 @@ void hmp_wavcapture(Monitor *mon, const QDict *qdict)
const char *audiodev = qdict_get_str(qdict, "audiodev"); const char *audiodev = qdict_get_str(qdict, "audiodev");
CaptureState *s; CaptureState *s;
Error *local_err = NULL; Error *local_err = NULL;
AudioState *as = audio_state_by_name(audiodev, &local_err); AudioBackend *as = audio_be_by_name(audiodev, &local_err);
warn_report_once("'wavcapture' is deprecated since v10.2, to be removed");
if (!as) { if (!as) {
error_report_err(local_err); error_report_err(local_err);

View file

@ -23,9 +23,8 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "audio.h" #include "qemu/audio.h"
#include "migration/vmstate.h" #include "migration/vmstate.h"
#include "monitor/monitor.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/clone-visitor.h" #include "qapi/clone-visitor.h"
@ -33,7 +32,6 @@
#include "qapi/qapi-visit-audio.h" #include "qapi/qapi-visit-audio.h"
#include "qapi/qapi-commands-audio.h" #include "qapi/qapi-commands-audio.h"
#include "qobject/qdict.h" #include "qobject/qdict.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/log.h" #include "qemu/log.h"
#include "qemu/module.h" #include "qemu/module.h"
@ -41,7 +39,6 @@
#include "system/system.h" #include "system/system.h"
#include "system/replay.h" #include "system/replay.h"
#include "system/runstate.h" #include "system/runstate.h"
#include "ui/qemu-spice.h"
#include "trace.h" #include "trace.h"
#define AUDIO_CAP "audio" #define AUDIO_CAP "audio"
@ -102,9 +99,7 @@ static audio_driver *audio_driver_lookup(const char *name)
return NULL; return NULL;
} }
static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states = static AudioBackend *default_audio_be;
QTAILQ_HEAD_INITIALIZER(audio_states);
static AudioState *default_audio_state;
const struct mixeng_volume nominal_volume = { const struct mixeng_volume nominal_volume = {
.mute = 0, .mute = 0,
@ -279,7 +274,7 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
&& info->is_signed == is_signed && info->is_signed == is_signed
&& info->is_float == is_float && info->is_float == is_float
&& info->bits == bits && info->bits == bits
&& info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS); && info->swap_endianness == (as->endianness != HOST_BIG_ENDIAN);
} }
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
@ -325,7 +320,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
info->nchannels = as->nchannels; info->nchannels = as->nchannels;
info->bytes_per_frame = as->nchannels * mul; info->bytes_per_frame = as->nchannels * mul;
info->bytes_per_second = info->freq * info->bytes_per_frame; info->bytes_per_second = info->freq * info->bytes_per_frame;
info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS); info->swap_endianness = (as->endianness != HOST_BIG_ENDIAN);
} }
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
@ -385,7 +380,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
/* /*
* Capture * Capture
*/ */
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s, static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioBackend *s,
struct audsettings *as) struct audsettings *as)
{ {
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
@ -410,7 +405,7 @@ static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
} }
} }
static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled) static void audio_capture_maybe_changed(CaptureVoiceOut *cap, bool enabled)
{ {
if (cap->hw.enabled != enabled) { if (cap->hw.enabled != enabled) {
audcnotification_e cmd; audcnotification_e cmd;
@ -424,11 +419,11 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
{ {
HWVoiceOut *hw = &cap->hw; HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw; SWVoiceOut *sw;
int enabled = 0; bool enabled = false;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) { if (sw->active) {
enabled = 1; enabled = true;
break; break;
} }
} }
@ -465,7 +460,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
static int audio_attach_capture (HWVoiceOut *hw) static int audio_attach_capture (HWVoiceOut *hw)
{ {
AudioState *s = hw->s; AudioBackend *s = hw->s;
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
audio_detach_capture (hw); audio_detach_capture (hw);
@ -480,7 +475,7 @@ static int audio_attach_capture (HWVoiceOut *hw)
sw = &sc->sw; sw = &sc->sw;
sw->hw = hw_cap; sw->hw = hw_cap;
sw->info = hw->info; sw->info = hw->info;
sw->empty = 1; sw->empty = true;
sw->active = hw->enabled; sw->active = hw->enabled;
sw->vol = nominal_volume; sw->vol = nominal_volume;
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq); sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
@ -803,7 +798,7 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
/* /*
* Timer * Timer
*/ */
static int audio_is_timer_needed(AudioState *s) static int audio_is_timer_needed(AudioBackend *s)
{ {
HWVoiceIn *hwi = NULL; HWVoiceIn *hwi = NULL;
HWVoiceOut *hwo = NULL; HWVoiceOut *hwo = NULL;
@ -821,7 +816,7 @@ static int audio_is_timer_needed(AudioState *s)
return 0; return 0;
} }
static void audio_reset_timer (AudioState *s) static void audio_reset_timer(AudioBackend *s)
{ {
if (audio_is_timer_needed(s)) { if (audio_is_timer_needed(s)) {
timer_mod_anticipate_ns(s->ts, timer_mod_anticipate_ns(s->ts,
@ -843,7 +838,7 @@ static void audio_reset_timer (AudioState *s)
static void audio_timer (void *opaque) static void audio_timer (void *opaque)
{ {
int64_t now, diff; int64_t now, diff;
AudioState *s = opaque; AudioBackend *s = opaque;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
diff = now - s->timer_last; diff = now - s->timer_last;
@ -916,7 +911,7 @@ int AUD_get_buffer_size_out(SWVoiceOut *sw)
return sw->hw->samples * sw->hw->info.bytes_per_frame; return sw->hw->samples * sw->hw->info.bytes_per_frame;
} }
void AUD_set_active_out (SWVoiceOut *sw, int on) void AUD_set_active_out(SWVoiceOut *sw, bool on)
{ {
HWVoiceOut *hw; HWVoiceOut *hw;
@ -926,14 +921,14 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw = sw->hw; hw = sw->hw;
if (sw->active != on) { if (sw->active != on) {
AudioState *s = sw->s; AudioBackend *s = sw->s;
SWVoiceOut *temp_sw; SWVoiceOut *temp_sw;
SWVoiceCap *sc; SWVoiceCap *sc;
if (on) { if (on) {
hw->pending_disable = 0; hw->pending_disable = 0;
if (!hw->enabled) { if (!hw->enabled) {
hw->enabled = 1; hw->enabled = true;
if (s->vm_running) { if (s->vm_running) {
if (hw->pcm_ops->enable_out) { if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, true); hw->pcm_ops->enable_out(hw, true);
@ -964,7 +959,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
} }
} }
void AUD_set_active_in (SWVoiceIn *sw, int on) void AUD_set_active_in(SWVoiceIn *sw, bool on)
{ {
HWVoiceIn *hw; HWVoiceIn *hw;
@ -974,12 +969,12 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
hw = sw->hw; hw = sw->hw;
if (sw->active != on) { if (sw->active != on) {
AudioState *s = sw->s; AudioBackend *s = sw->s;
SWVoiceIn *temp_sw; SWVoiceIn *temp_sw;
if (on) { if (on) {
if (!hw->enabled) { if (!hw->enabled) {
hw->enabled = 1; hw->enabled = true;
if (s->vm_running) { if (s->vm_running) {
if (hw->pcm_ops->enable_in) { if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, true); hw->pcm_ops->enable_in(hw, true);
@ -998,7 +993,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
} }
if (nb_active == 1) { if (nb_active == 1) {
hw->enabled = 0; hw->enabled = false;
if (hw->pcm_ops->enable_in) { if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, false); hw->pcm_ops->enable_in(hw, false);
} }
@ -1142,7 +1137,7 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
return clipped; return clipped;
} }
static void audio_run_out (AudioState *s) static void audio_run_out(AudioBackend *s)
{ {
HWVoiceOut *hw = NULL; HWVoiceOut *hw = NULL;
SWVoiceOut *sw; SWVoiceOut *sw;
@ -1157,8 +1152,8 @@ static void audio_run_out (AudioState *s)
sw = hw->sw_head.lh_first; sw = hw->sw_head.lh_first;
if (hw->pending_disable) { if (hw->pending_disable) {
hw->enabled = 0; hw->enabled = false;
hw->pending_disable = 0; hw->pending_disable = false;
if (hw->pcm_ops->enable_out) { if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false); hw->pcm_ops->enable_out(hw, false);
} }
@ -1211,13 +1206,13 @@ static void audio_run_out (AudioState *s)
#ifdef DEBUG_OUT #ifdef DEBUG_OUT
dolog ("Disabling voice\n"); dolog ("Disabling voice\n");
#endif #endif
hw->enabled = 0; hw->enabled = false;
hw->pending_disable = 0; hw->pending_disable = false;
if (hw->pcm_ops->enable_out) { if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false); hw->pcm_ops->enable_out(hw, false);
} }
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
sc->sw.active = 0; sc->sw.active = false;
audio_recalc_and_notify_capture (sc->cap); audio_recalc_and_notify_capture (sc->cap);
} }
continue; continue;
@ -1262,7 +1257,7 @@ static void audio_run_out (AudioState *s)
sw->total_hw_samples_mixed -= played; sw->total_hw_samples_mixed -= played;
if (!sw->total_hw_samples_mixed) { if (!sw->total_hw_samples_mixed) {
sw->empty = 1; sw->empty = true;
} }
} }
} }
@ -1296,7 +1291,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
return conv; return conv;
} }
static void audio_run_in (AudioState *s) static void audio_run_in(AudioBackend *s)
{ {
HWVoiceIn *hw = NULL; HWVoiceIn *hw = NULL;
@ -1344,7 +1339,7 @@ static void audio_run_in (AudioState *s)
} }
} }
static void audio_run_capture (AudioState *s) static void audio_run_capture(AudioBackend *s)
{ {
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
@ -1391,7 +1386,7 @@ static void audio_run_capture (AudioState *s)
} }
} }
void audio_run(AudioState *s, const char *msg) void audio_run(AudioBackend *s, const char *msg)
{ {
audio_run_out(s); audio_run_out(s);
audio_run_in(s); audio_run_in(s);
@ -1564,14 +1559,14 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
return total; return total;
} }
static int audio_driver_init(AudioState *s, struct audio_driver *drv, static bool audio_driver_init(AudioBackend *s, struct audio_driver *drv,
Audiodev *dev, Error **errp) Audiodev *dev, Error **errp)
{ {
Error *local_err = NULL; s->drv_opaque = drv->init(dev, errp);
if (!s->drv_opaque) {
return false;
}
s->drv_opaque = drv->init(dev, &local_err);
if (s->drv_opaque) {
if (!drv->pcm_ops->get_buffer_in) { if (!drv->pcm_ops->get_buffer_in) {
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in; drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in; drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
@ -1584,21 +1579,20 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv,
audio_init_nb_voices_out(s, drv, 1); audio_init_nb_voices_out(s, drv, 1);
audio_init_nb_voices_in(s, drv, 0); audio_init_nb_voices_in(s, drv, 0);
s->drv = drv; s->drv = drv;
return 0;
if (dev->timer_period <= 0) {
s->period_ticks = 1;
} else { } else {
if (local_err) { s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
error_propagate(errp, local_err);
} else {
error_setg(errp, "Could not init `%s' audio driver", drv->name);
}
return -1;
} }
return true;
} }
static void audio_vm_change_state_handler (void *opaque, bool running, static void audio_vm_change_state_handler (void *opaque, bool running,
RunState state) RunState state)
{ {
AudioState *s = opaque; AudioBackend *s = opaque;
HWVoiceOut *hwo = NULL; HWVoiceOut *hwo = NULL;
HWVoiceIn *hwi = NULL; HWVoiceIn *hwi = NULL;
@ -1617,8 +1611,26 @@ static void audio_vm_change_state_handler (void *opaque, bool running,
audio_reset_timer (s); audio_reset_timer (s);
} }
static void free_audio_state(AudioState *s) static const VMStateDescription vmstate_audio;
static void audio_be_init(Object *obj)
{ {
AudioBackend *s = AUDIO_BACKEND(obj);
QLIST_INIT(&s->hw_head_out);
QLIST_INIT(&s->hw_head_in);
QLIST_INIT(&s->cap_head);
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
s->vmse = qemu_add_vm_change_state_handler(audio_vm_change_state_handler, s);
assert(s->vmse != NULL);
vmstate_register_any(NULL, &vmstate_audio, s);
}
static void audio_be_finalize(Object *obj)
{
AudioBackend *s = AUDIO_BACKEND(obj);
HWVoiceOut *hwo, *hwon; HWVoiceOut *hwo, *hwon;
HWVoiceIn *hwi, *hwin; HWVoiceIn *hwi, *hwin;
@ -1664,17 +1676,24 @@ static void free_audio_state(AudioState *s)
s->ts = NULL; s->ts = NULL;
} }
g_free(s); if (s->vmse) {
qemu_del_vm_change_state_handler(s->vmse);
s->vmse = NULL;
}
vmstate_unregister(NULL, &vmstate_audio, s);
}
static Object *get_audiodevs_root(void)
{
return object_get_container("audiodevs");
} }
void audio_cleanup(void) void audio_cleanup(void)
{ {
default_audio_state = NULL; default_audio_be = NULL;
while (!QTAILQ_EMPTY(&audio_states)) {
AudioState *s = QTAILQ_FIRST(&audio_states); object_unparent(get_audiodevs_root());
QTAILQ_REMOVE(&audio_states, s, list);
free_audio_state(s);
}
} }
static bool vmstate_audio_needed(void *opaque) static bool vmstate_audio_needed(void *opaque)
@ -1712,7 +1731,7 @@ void audio_create_default_audiodevs(void)
visit_type_Audiodev(v, NULL, &dev, &error_fatal); visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v); visit_free(v);
audio_define_default(dev, &error_abort); audio_add_default_audiodev(dev, &error_abort);
} }
} }
} }
@ -1723,26 +1742,14 @@ void audio_create_default_audiodevs(void)
* if dev == NULL => legacy implicit initialization, return the already created * if dev == NULL => legacy implicit initialization, return the already created
* state or create a new one * state or create a new one
*/ */
static AudioState *audio_init(Audiodev *dev, Error **errp) static AudioBackend *audio_init(Audiodev *dev, Error **errp)
{ {
static bool atexit_registered;
int done = 0; int done = 0;
const char *drvname; const char *drvname;
VMChangeStateEntry *vmse; AudioBackend *s;
AudioState *s;
struct audio_driver *driver; struct audio_driver *driver;
s = g_new0(AudioState, 1); s = AUDIO_BACKEND(object_new(TYPE_AUDIO_BACKEND));
QLIST_INIT (&s->hw_head_out);
QLIST_INIT (&s->hw_head_in);
QLIST_INIT (&s->cap_head);
if (!atexit_registered) {
atexit(audio_cleanup);
atexit_registered = true;
}
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
if (dev) { if (dev) {
/* -audiodev option */ /* -audiodev option */
@ -1750,7 +1757,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
drvname = AudiodevDriver_str(dev->driver); drvname = AudiodevDriver_str(dev->driver);
driver = audio_driver_lookup(drvname); driver = audio_driver_lookup(drvname);
if (driver) { if (driver) {
done = !audio_driver_init(s, driver, dev, errp); done = audio_driver_init(s, driver, dev, errp);
} else { } else {
error_setg(errp, "Unknown audio driver `%s'", drvname); error_setg(errp, "Unknown audio driver `%s'", drvname);
} }
@ -1758,7 +1765,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
goto out; goto out;
} }
} else { } else {
assert(!default_audio_state); assert(!default_audio_be);
for (;;) { for (;;) {
AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs); AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
if (!e) { if (!e) {
@ -1770,7 +1777,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
g_free(e); g_free(e);
drvname = AudiodevDriver_str(dev->driver); drvname = AudiodevDriver_str(dev->driver);
driver = audio_driver_lookup(drvname); driver = audio_driver_lookup(drvname);
if (!audio_driver_init(s, driver, dev, NULL)) { if (audio_driver_init(s, driver, dev, NULL)) {
break; break;
} }
qapi_free_Audiodev(dev); qapi_free_Audiodev(dev);
@ -1778,33 +1785,22 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
} }
} }
if (dev->timer_period <= 0) { if (!object_property_try_add_child(get_audiodevs_root(), dev->id, OBJECT(s), errp)) {
s->period_ticks = 1; goto out;
} else {
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
} }
object_unref(s);
vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
if (!vmse) {
dolog ("warning: Could not register change state handler\n"
"(Audio can continue looping even after stopping the VM)\n");
}
QTAILQ_INSERT_TAIL(&audio_states, s, list);
QLIST_INIT (&s->card_head);
vmstate_register_any(NULL, &vmstate_audio, s);
return s; return s;
out: out:
free_audio_state(s); object_unref(s);
return NULL; return NULL;
} }
AudioState *audio_get_default_audio_state(Error **errp) AudioBackend *audio_get_default_audio_be(Error **errp)
{ {
if (!default_audio_state) { if (!default_audio_be) {
default_audio_state = audio_init(NULL, errp); default_audio_be = audio_init(NULL, errp);
if (!default_audio_state) { if (!default_audio_be) {
if (!QSIMPLEQ_EMPTY(&audiodevs)) { if (!QSIMPLEQ_EMPTY(&audiodevs)) {
error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n", error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n",
QSIMPLEQ_FIRST(&audiodevs)->dev->id); QSIMPLEQ_FIRST(&audiodevs)->dev->id);
@ -1812,35 +1808,27 @@ AudioState *audio_get_default_audio_state(Error **errp)
} }
} }
return default_audio_state; return default_audio_be;
} }
bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp) bool AUD_backend_check(AudioBackend **be, Error **errp)
{ {
if (!card->state) { assert(be != NULL);
card->state = audio_get_default_audio_state(errp);
if (!card->state) { if (!*be) {
*be = audio_get_default_audio_be(errp);
if (!*be) {
return false; return false;
} }
} }
card->name = g_strdup (name);
memset (&card->entries, 0, sizeof (card->entries));
QLIST_INSERT_HEAD(&card->state->card_head, card, entries);
return true; return true;
} }
void AUD_remove_card (QEMUSoundCard *card)
{
QLIST_REMOVE (card, entries);
g_free (card->name);
}
static struct audio_pcm_ops capture_pcm_ops; static struct audio_pcm_ops capture_pcm_ops;
CaptureVoiceOut *AUD_add_capture( CaptureVoiceOut *AUD_add_capture(
AudioState *s, AudioBackend *s,
struct audsettings *as, struct audsettings *as,
struct audio_capture_ops *ops, struct audio_capture_ops *ops,
void *cb_opaque void *cb_opaque
@ -1952,13 +1940,7 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
} }
} }
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol) void AUD_set_volume_out(SWVoiceOut *sw, Volume *vol)
{
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
audio_set_volume_out(sw, &vol);
}
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
{ {
if (sw) { if (sw) {
HWVoiceOut *hw = sw->hw; HWVoiceOut *hw = sw->hw;
@ -1974,13 +1956,7 @@ void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
} }
} }
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol) void AUD_set_volume_in(SWVoiceIn *sw, Volume *vol)
{
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
audio_set_volume_in(sw, &vol);
}
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol)
{ {
if (sw) { if (sw) {
HWVoiceIn *hw = sw->hw; HWVoiceIn *hw = sw->hw;
@ -2142,10 +2118,10 @@ void audio_parse_option(const char *opt)
visit_type_Audiodev(v, NULL, &dev, &error_fatal); visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v); visit_free(v);
audio_define(dev); audio_add_audiodev(dev);
} }
void audio_define(Audiodev *dev) void audio_add_audiodev(Audiodev *dev)
{ {
AudiodevListEntry *e; AudiodevListEntry *e;
@ -2156,7 +2132,7 @@ void audio_define(Audiodev *dev)
QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next); QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
} }
void audio_define_default(Audiodev *dev, Error **errp) void audio_add_default_audiodev(Audiodev *dev, Error **errp)
{ {
AudiodevListEntry *e; AudiodevListEntry *e;
@ -2182,7 +2158,7 @@ audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
.freq = pdo->frequency, .freq = pdo->frequency,
.nchannels = pdo->channels, .nchannels = pdo->channels,
.fmt = pdo->format, .fmt = pdo->format,
.endianness = AUDIO_HOST_ENDIANNESS, .endianness = HOST_BIG_ENDIAN,
}; };
} }
@ -2235,24 +2211,40 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audioformat_bytes_per_sample(as->fmt); audioformat_bytes_per_sample(as->fmt);
} }
AudioState *audio_state_by_name(const char *name, Error **errp) AudioBackend *audio_be_by_name(const char *name, Error **errp)
{ {
AudioState *s; Object *obj = object_resolve_path_component(get_audiodevs_root(), name);
QTAILQ_FOREACH(s, &audio_states, list) {
assert(s->dev); if (!obj) {
if (strcmp(name, s->dev->id) == 0) {
return s;
}
}
error_setg(errp, "audiodev '%s' not found", name); error_setg(errp, "audiodev '%s' not found", name);
return NULL; return NULL;
} else {
return AUDIO_BACKEND(obj);
}
} }
const char *audio_get_id(QEMUSoundCard *card) #ifdef CONFIG_GIO
bool audio_be_set_dbus_server(AudioBackend *be,
GDBusObjectManagerServer *server,
bool p2p,
Error **errp)
{ {
if (card->state) { assert(be != NULL);
assert(card->state->dev);
return card->state->dev->id; if (!be->drv->set_dbus_server) {
error_setg(errp, "Audiodev '%s' is not compatible with DBus", be->dev->id);
return false;
}
return be->drv->set_dbus_server(be, server, p2p, errp);
}
#endif
const char *audio_be_get_id(AudioBackend *be)
{
if (be) {
assert(be->dev);
return be->dev->id;
} else { } else {
return ""; return "";
} }
@ -2320,3 +2312,20 @@ AudiodevList *qmp_query_audiodevs(Error **errp)
} }
return ret; return ret;
} }
static const TypeInfo audio_be_info = {
.name = TYPE_AUDIO_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(AudioBackend),
.instance_init = audio_be_init,
.instance_finalize = audio_be_finalize,
.abstract = false, /* TODO: subclass drivers and make it abstract */
.class_size = sizeof(AudioBackendClass),
};
static void register_types(void)
{
type_register_static(&audio_be_info);
}
type_init(register_types);

View file

@ -1,185 +0,0 @@
/*
* QEMU Audio subsystem header
*
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef QEMU_AUDIO_H
#define QEMU_AUDIO_H
#include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
typedef void (*audio_callback_fn) (void *opaque, int avail);
#if HOST_BIG_ENDIAN
#define AUDIO_HOST_ENDIANNESS 1
#else
#define AUDIO_HOST_ENDIANNESS 0
#endif
typedef struct audsettings {
int freq;
int nchannels;
AudioFormat fmt;
int endianness;
} audsettings;
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
typedef enum {
AUD_CNOTIFY_ENABLE,
AUD_CNOTIFY_DISABLE
} audcnotification_e;
struct audio_capture_ops {
void (*notify) (void *opaque, audcnotification_e cmd);
void (*capture) (void *opaque, const void *buf, int size);
void (*destroy) (void *opaque);
};
struct capture_ops {
void (*info) (void *opaque);
void (*destroy) (void *opaque);
};
typedef struct CaptureState {
void *opaque;
struct capture_ops ops;
QLIST_ENTRY (CaptureState) entries;
} CaptureState;
typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;
typedef struct AudioState AudioState;
typedef struct QEMUSoundCard {
char *name;
AudioState *state;
QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard;
typedef struct QEMUAudioTimeStamp {
uint64_t old_ts;
} QEMUAudioTimeStamp;
void AUD_vlog (const char *cap, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) G_GNUC_PRINTF(2, 3);
bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp);
void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture(
AudioState *s,
struct audsettings *as,
struct audio_capture_ops *ops,
void *opaque
);
void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
SWVoiceOut *AUD_open_out (
QEMUSoundCard *card,
SWVoiceOut *sw,
const char *name,
void *callback_opaque,
audio_callback_fn callback_fn,
struct audsettings *settings
);
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw);
void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
#define AUDIO_MAX_CHANNELS 16
typedef struct Volume {
bool mute;
int channels;
uint8_t vol[AUDIO_MAX_CHANNELS];
} Volume;
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol);
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol);
SWVoiceIn *AUD_open_in (
QEMUSoundCard *card,
SWVoiceIn *sw,
const char *name,
void *callback_opaque,
audio_callback_fn callback_fn,
struct audsettings *settings
);
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw);
void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
static inline void *advance (void *p, int incr)
{
uint8_t *d = p;
return (d + incr);
}
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels);
void audio_cleanup(void);
void audio_sample_to_uint64(const void *samples, int pos,
uint64_t *left, uint64_t *right);
void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right);
void audio_define(Audiodev *audio);
void audio_define_default(Audiodev *dev, Error **errp);
void audio_parse_option(const char *opt);
void audio_create_default_audiodevs(void);
void audio_init_audiodevs(void);
void audio_help(void);
AudioState *audio_state_by_name(const char *name, Error **errp);
AudioState *audio_get_default_audio_state(Error **errp);
const char *audio_get_id(QEMUSoundCard *card);
#define DEFINE_AUDIO_PROPERTIES(_s, _f) \
DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
#endif /* QEMU_AUDIO_H */

View file

@ -29,12 +29,20 @@
#define FLOAT_MIXENG #define FLOAT_MIXENG
/* #define RECIPROCAL */ /* #define RECIPROCAL */
#endif #endif
#include "qemu/audio.h"
#include "qemu/audio-capture.h"
#include "mixeng.h" #include "mixeng.h"
#ifdef CONFIG_GIO #ifdef CONFIG_GIO
#include <gio/gio.h> #include <gio/gio.h>
#endif #endif
void G_GNUC_PRINTF(2, 0)
AUD_vlog(const char *cap, const char *fmt, va_list ap);
void G_GNUC_PRINTF(2, 3)
AUD_log(const char *cap, const char *fmt, ...);
struct audio_pcm_ops; struct audio_pcm_ops;
struct audio_callback { struct audio_callback {
@ -53,7 +61,7 @@ struct audio_pcm_info {
int swap_endianness; int swap_endianness;
}; };
typedef struct AudioState AudioState; typedef struct AudioBackend AudioBackend;
typedef struct SWVoiceCap SWVoiceCap; typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer { typedef struct STSampleBuffer {
@ -62,10 +70,10 @@ typedef struct STSampleBuffer {
} STSampleBuffer; } STSampleBuffer;
typedef struct HWVoiceOut { typedef struct HWVoiceOut {
AudioState *s; AudioBackend *s;
int enabled; bool enabled;
int poll_mode; int poll_mode;
int pending_disable; bool pending_disable;
struct audio_pcm_info info; struct audio_pcm_info info;
f_sample *clip; f_sample *clip;
@ -83,8 +91,8 @@ typedef struct HWVoiceOut {
} HWVoiceOut; } HWVoiceOut;
typedef struct HWVoiceIn { typedef struct HWVoiceIn {
AudioState *s; AudioBackend *s;
int enabled; bool enabled;
int poll_mode; int poll_mode;
struct audio_pcm_info info; struct audio_pcm_info info;
@ -104,15 +112,14 @@ typedef struct HWVoiceIn {
} HWVoiceIn; } HWVoiceIn;
struct SWVoiceOut { struct SWVoiceOut {
QEMUSoundCard *card; AudioBackend *s;
AudioState *s;
struct audio_pcm_info info; struct audio_pcm_info info;
t_sample *conv; t_sample *conv;
STSampleBuffer resample_buf; STSampleBuffer resample_buf;
void *rate; void *rate;
size_t total_hw_samples_mixed; size_t total_hw_samples_mixed;
int active; bool active;
int empty; bool empty;
HWVoiceOut *hw; HWVoiceOut *hw;
char *name; char *name;
struct mixeng_volume vol; struct mixeng_volume vol;
@ -121,9 +128,8 @@ struct SWVoiceOut {
}; };
struct SWVoiceIn { struct SWVoiceIn {
QEMUSoundCard *card; AudioBackend *s;
AudioState *s; bool active;
int active;
struct audio_pcm_info info; struct audio_pcm_info info;
void *rate; void *rate;
size_t total_hw_samples_acquired; size_t total_hw_samples_acquired;
@ -139,11 +145,13 @@ struct SWVoiceIn {
typedef struct audio_driver audio_driver; typedef struct audio_driver audio_driver;
struct audio_driver { struct audio_driver {
const char *name; const char *name;
const char *descr;
void *(*init) (Audiodev *, Error **); void *(*init) (Audiodev *, Error **);
void (*fini) (void *); void (*fini) (void *);
#ifdef CONFIG_GIO #ifdef CONFIG_GIO
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager, bool p2p); bool (*set_dbus_server)(AudioBackend *be,
GDBusObjectManagerServer *manager,
bool p2p,
Error **errp);
#endif #endif
struct audio_pcm_ops *pcm_ops; struct audio_pcm_ops *pcm_ops;
int max_voices_out; int max_voices_out;
@ -187,6 +195,23 @@ struct audio_pcm_ops {
void (*volume_in)(HWVoiceIn *hw, Volume *vol); void (*volume_in)(HWVoiceIn *hw, Volume *vol);
}; };
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
static inline void *advance(void *p, size_t incr)
{
return (uint8_t *)p + incr;
}
int wav_start_capture(AudioBackend *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels);
void audio_generic_run_buffer_in(HWVoiceIn *hw); void audio_generic_run_buffer_in(HWVoiceIn *hw);
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size); void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size); void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
@ -216,13 +241,14 @@ struct SWVoiceCap {
QLIST_ENTRY (SWVoiceCap) entries; QLIST_ENTRY (SWVoiceCap) entries;
}; };
typedef struct AudioState { typedef struct AudioBackend {
Object parent;
struct audio_driver *drv; struct audio_driver *drv;
Audiodev *dev; Audiodev *dev;
void *drv_opaque; void *drv_opaque;
QEMUTimer *ts; QEMUTimer *ts;
QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head; QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
@ -233,9 +259,8 @@ typedef struct AudioState {
bool timer_running; bool timer_running;
uint64_t timer_last; uint64_t timer_last;
VMChangeStateEntry *vmse;
QTAILQ_ENTRY(AudioState) list; } AudioBackend;
} AudioState;
extern const struct mixeng_volume nominal_volume; extern const struct mixeng_volume nominal_volume;
@ -248,7 +273,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
int audio_bug (const char *funcname, int cond); int audio_bug (const char *funcname, int cond);
void audio_run(AudioState *s, const char *msg); void audio_run(AudioBackend *s, const char *msg);
const char *audio_application_name(void); const char *audio_application_name(void);

View file

@ -36,7 +36,7 @@
#define HWBUF hw->conv_buf #define HWBUF hw->conv_buf
#endif #endif
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, static void glue(audio_init_nb_voices_, TYPE)(AudioBackend *s,
struct audio_driver *drv, int min_voices) struct audio_driver *drv, int min_voices)
{ {
int max_voices = glue (drv->max_voices_, TYPE); int max_voices = glue (drv->max_voices_, TYPE);
@ -166,10 +166,10 @@ static int glue (audio_pcm_sw_init_, TYPE) (
audio_pcm_init_info (&sw->info, as); audio_pcm_init_info (&sw->info, as);
sw->hw = hw; sw->hw = hw;
sw->active = 0; sw->active = false;
#ifdef DAC #ifdef DAC
sw->total_hw_samples_mixed = 0; sw->total_hw_samples_mixed = 0;
sw->empty = 1; sw->empty = true;
#endif #endif
if (sw->info.is_float) { if (sw->info.is_float) {
@ -221,7 +221,7 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{ {
HW *hw = *hwp; HW *hw = *hwp;
AudioState *s = hw->s; AudioBackend *s = hw->s;
if (!hw->sw_head.lh_first) { if (!hw->sw_head.lh_first) {
#ifdef DAC #ifdef DAC
@ -236,12 +236,12 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
} }
} }
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw) static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioBackend *s, HW *hw)
{ {
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first; return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
} }
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw) static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioBackend *s, HW *hw)
{ {
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
if (hw->enabled) { if (hw->enabled) {
@ -251,7 +251,7 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
return NULL; return NULL;
} }
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw, static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioBackend *s, HW *hw,
struct audsettings *as) struct audsettings *as)
{ {
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
@ -262,7 +262,7 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
return NULL; return NULL;
} }
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioBackend *s,
struct audsettings *as) struct audsettings *as)
{ {
HW *hw; HW *hw;
@ -398,7 +398,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
abort(); abort();
} }
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as) static HW *glue(audio_pcm_hw_add_, TYPE)(AudioBackend *s, struct audsettings *as)
{ {
HW *hw; HW *hw;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
@ -424,7 +424,7 @@ static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
} }
static SW *glue(audio_pcm_create_voice_pair_, TYPE)( static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
AudioState *s, AudioBackend *s,
const char *sw_name, const char *sw_name,
struct audsettings *as struct audsettings *as
) )
@ -473,11 +473,11 @@ static void glue (audio_close_, TYPE) (SW *sw)
g_free (sw); g_free (sw);
} }
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) void glue(AUD_close_, TYPE)(AudioBackend *be, SW *sw)
{ {
if (sw) { if (sw) {
if (audio_bug(__func__, !card)) { if (audio_bug(__func__, !be)) {
dolog ("card=%p\n", card); dolog("backend=%p\n", be);
return; return;
} }
@ -486,7 +486,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
} }
SW *glue (AUD_open_, TYPE) ( SW *glue (AUD_open_, TYPE) (
QEMUSoundCard *card, AudioBackend *be,
SW *sw, SW *sw,
const char *name, const char *name,
void *callback_opaque , void *callback_opaque ,
@ -494,16 +494,15 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as struct audsettings *as
) )
{ {
AudioState *s; AudioBackend *s = be;
AudiodevPerDirectionOptions *pdo; AudiodevPerDirectionOptions *pdo;
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { if (audio_bug(__func__, !be || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n", dolog("backend=%p name=%p callback_fn=%p as=%p\n",
card, name, callback_fn, as); be, name, callback_fn, as);
goto fail; goto fail;
} }
s = card->state;
pdo = glue(audio_get_pdo_, TYPE)(s->dev); pdo = glue(audio_get_pdo_, TYPE)(s->dev);
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n", ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
@ -524,7 +523,7 @@ SW *glue (AUD_open_, TYPE) (
} }
if (!pdo->fixed_settings && sw) { if (!pdo->fixed_settings && sw) {
glue (AUD_close_, TYPE) (card, sw); glue(AUD_close_, TYPE)(be, sw);
sw = NULL; sw = NULL;
} }
@ -548,7 +547,6 @@ SW *glue (AUD_open_, TYPE) (
} }
} }
sw->card = card;
sw->vol = nominal_volume; sw->vol = nominal_volume;
sw->callback.fn = callback_fn; sw->callback.fn = callback_fn;
sw->callback.opaque = callback_opaque; sw->callback.opaque = callback_opaque;
@ -562,11 +560,11 @@ SW *glue (AUD_open_, TYPE) (
return sw; return sw;
fail: fail:
glue (AUD_close_, TYPE) (card, sw); glue(AUD_close_, TYPE)(be, sw);
return NULL; return NULL;
} }
int glue (AUD_is_active_, TYPE) (SW *sw) bool glue(AUD_is_active_, TYPE)(SW *sw)
{ {
return sw ? sw->active : 0; return sw ? sw->active : 0;
} }

View file

@ -7,7 +7,7 @@
#include <mmreg.h> #include <mmreg.h>
#include <mmsystem.h> #include <mmsystem.h>
#include "audio.h" #include "qemu/audio.h"
#include "audio_int.h" #include "audio_int.h"
#include "audio_win_int.h" #include "audio_win_int.h"

View file

@ -28,7 +28,7 @@
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "audio.h" #include "qemu/audio.h"
#define AUDIO_CAP "coreaudio" #define AUDIO_CAP "coreaudio"
#include "audio_int.h" #include "audio_int.h"
@ -664,7 +664,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
static struct audio_driver coreaudio_audio_driver = { static struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio", .name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
.init = coreaudio_audio_init, .init = coreaudio_audio_init,
.fini = coreaudio_audio_fini, .fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops, .pcm_ops = &coreaudio_pcm_ops,

View file

@ -24,9 +24,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/host-utils.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/timer.h"
#include "qemu/dbus.h" #include "qemu/dbus.h"
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
@ -37,7 +35,7 @@
#include "ui/dbus-display1.h" #include "ui/dbus-display1.h"
#define AUDIO_CAP "dbus" #define AUDIO_CAP "dbus"
#include "audio.h" #include "qemu/audio.h"
#include "audio_int.h" #include "audio_int.h"
#include "trace.h" #include "trace.h"
@ -460,7 +458,7 @@ listener_in_vanished_cb(GDBusConnection *connection,
} }
static gboolean static gboolean
dbus_audio_register_listener(AudioState *s, dbus_audio_register_listener(AudioBackend *s,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
GUnixFDList *fd_list, GUnixFDList *fd_list,
@ -617,7 +615,7 @@ dbus_audio_register_listener(AudioState *s,
} }
static gboolean static gboolean
dbus_audio_register_out_listener(AudioState *s, dbus_audio_register_out_listener(AudioBackend *s,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
GUnixFDList *fd_list, GUnixFDList *fd_list,
@ -633,7 +631,7 @@ dbus_audio_register_out_listener(AudioState *s,
} }
static gboolean static gboolean
dbus_audio_register_in_listener(AudioState *s, dbus_audio_register_in_listener(AudioBackend *s,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
GUnixFDList *fd_list, GUnixFDList *fd_list,
@ -647,8 +645,11 @@ dbus_audio_register_in_listener(AudioState *s,
arg_listener, false); arg_listener, false);
} }
static void static bool
dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) dbus_audio_set_server(AudioBackend *s,
GDBusObjectManagerServer *server,
bool p2p,
Error **errp)
{ {
DBusAudio *da = s->drv_opaque; DBusAudio *da = s->drv_opaque;
@ -671,6 +672,8 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio), g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
G_DBUS_INTERFACE_SKELETON(da->iface)); G_DBUS_INTERFACE_SKELETON(da->iface));
g_dbus_object_manager_server_export(da->server, da->audio); g_dbus_object_manager_server_export(da->server, da->audio);
return true;
} }
static struct audio_pcm_ops dbus_pcm_ops = { static struct audio_pcm_ops dbus_pcm_ops = {
@ -692,7 +695,6 @@ static struct audio_pcm_ops dbus_pcm_ops = {
static struct audio_driver dbus_audio_driver = { static struct audio_driver dbus_audio_driver = {
.name = "dbus", .name = "dbus",
.descr = "Timer based audio exposed with DBus interface",
.init = dbus_audio_init, .init = dbus_audio_init,
.fini = dbus_audio_fini, .fini = dbus_audio_fini,
.set_dbus_server = dbus_audio_set_server, .set_dbus_server = dbus_audio_set_server,

View file

@ -27,12 +27,12 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "audio.h" #include "qemu/audio.h"
#define AUDIO_CAP "dsound" #define AUDIO_CAP "dsound"
#include "audio_int.h" #include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qapi/error.h"
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
@ -64,162 +64,154 @@ typedef struct {
dsound *s; dsound *s;
} DSoundVoiceIn; } DSoundVoiceIn;
static void dsound_log_hresult (HRESULT hr) static const char *dserror(HRESULT hr)
{ {
const char *str = "BUG";
switch (hr) { switch (hr) {
case DS_OK: case DS_OK:
str = "The method succeeded"; return "The method succeeded";
break;
#ifdef DS_NO_VIRTUALIZATION #ifdef DS_NO_VIRTUALIZATION
case DS_NO_VIRTUALIZATION: case DS_NO_VIRTUALIZATION:
str = "The buffer was created, but another 3D algorithm was substituted"; return "The buffer was created, but another 3D algorithm was substituted";
break;
#endif #endif
#ifdef DS_INCOMPLETE #ifdef DS_INCOMPLETE
case DS_INCOMPLETE: case DS_INCOMPLETE:
str = "The method succeeded, but not all the optional effects were obtained"; return "The method succeeded, but not all the optional effects were obtained";
break;
#endif #endif
#ifdef DSERR_ACCESSDENIED #ifdef DSERR_ACCESSDENIED
case DSERR_ACCESSDENIED: case DSERR_ACCESSDENIED:
str = "The request failed because access was denied"; return "The request failed because access was denied";
break;
#endif #endif
#ifdef DSERR_ALLOCATED #ifdef DSERR_ALLOCATED
case DSERR_ALLOCATED: case DSERR_ALLOCATED:
str = "The request failed because resources, " return "The request failed because resources, "
"such as a priority level, were already in use " "such as a priority level, were already in use "
"by another caller"; "by another caller";
break;
#endif #endif
#ifdef DSERR_ALREADYINITIALIZED #ifdef DSERR_ALREADYINITIALIZED
case DSERR_ALREADYINITIALIZED: case DSERR_ALREADYINITIALIZED:
str = "The object is already initialized"; return "The object is already initialized";
break;
#endif #endif
#ifdef DSERR_BADFORMAT #ifdef DSERR_BADFORMAT
case DSERR_BADFORMAT: case DSERR_BADFORMAT:
str = "The specified wave format is not supported"; return "The specified wave format is not supported";
break;
#endif #endif
#ifdef DSERR_BADSENDBUFFERGUID #ifdef DSERR_BADSENDBUFFERGUID
case DSERR_BADSENDBUFFERGUID: case DSERR_BADSENDBUFFERGUID:
str = "The GUID specified in an audiopath file " return "The GUID specified in an audiopath file "
"does not match a valid mix-in buffer"; "does not match a valid mix-in buffer";
break;
#endif #endif
#ifdef DSERR_BUFFERLOST #ifdef DSERR_BUFFERLOST
case DSERR_BUFFERLOST: case DSERR_BUFFERLOST:
str = "The buffer memory has been lost and must be restored"; return "The buffer memory has been lost and must be restored";
break;
#endif #endif
#ifdef DSERR_BUFFERTOOSMALL #ifdef DSERR_BUFFERTOOSMALL
case DSERR_BUFFERTOOSMALL: case DSERR_BUFFERTOOSMALL:
str = "The buffer size is not great enough to " return "The buffer size is not great enough to "
"enable effects processing"; "enable effects processing";
break;
#endif #endif
#ifdef DSERR_CONTROLUNAVAIL #ifdef DSERR_CONTROLUNAVAIL
case DSERR_CONTROLUNAVAIL: case DSERR_CONTROLUNAVAIL:
str = "The buffer control (volume, pan, and so on) " return "The buffer control (volume, pan, and so on) "
"requested by the caller is not available. " "requested by the caller is not available. "
"Controls must be specified when the buffer is created, " "Controls must be specified when the buffer is created, "
"using the dwFlags member of DSBUFFERDESC"; "using the dwFlags member of DSBUFFERDESC";
break;
#endif #endif
#ifdef DSERR_DS8_REQUIRED #ifdef DSERR_DS8_REQUIRED
case DSERR_DS8_REQUIRED: case DSERR_DS8_REQUIRED:
str = "A DirectSound object of class CLSID_DirectSound8 or later " return "A DirectSound object of class CLSID_DirectSound8 or later "
"is required for the requested functionality. " "is required for the requested functionality. "
"For more information, see IDirectSound8 Interface"; "For more information, see IDirectSound8 Interface";
break;
#endif #endif
#ifdef DSERR_FXUNAVAILABLE #ifdef DSERR_FXUNAVAILABLE
case DSERR_FXUNAVAILABLE: case DSERR_FXUNAVAILABLE:
str = "The effects requested could not be found on the system, " return "The effects requested could not be found on the system, "
"or they are in the wrong order or in the wrong location; " "or they are in the wrong order or in the wrong location; "
"for example, an effect expected in hardware " "for example, an effect expected in hardware "
"was found in software"; "was found in software";
break;
#endif #endif
#ifdef DSERR_GENERIC #ifdef DSERR_GENERIC
case DSERR_GENERIC: case DSERR_GENERIC:
str = "An undetermined error occurred inside the DirectSound subsystem"; return "An undetermined error occurred inside the DirectSound subsystem";
break;
#endif #endif
#ifdef DSERR_INVALIDCALL #ifdef DSERR_INVALIDCALL
case DSERR_INVALIDCALL: case DSERR_INVALIDCALL:
str = "This function is not valid for the current state of this object"; return "This function is not valid for the current state of this object";
break;
#endif #endif
#ifdef DSERR_INVALIDPARAM #ifdef DSERR_INVALIDPARAM
case DSERR_INVALIDPARAM: case DSERR_INVALIDPARAM:
str = "An invalid parameter was passed to the returning function"; return "An invalid parameter was passed to the returning function";
break;
#endif #endif
#ifdef DSERR_NOAGGREGATION #ifdef DSERR_NOAGGREGATION
case DSERR_NOAGGREGATION: case DSERR_NOAGGREGATION:
str = "The object does not support aggregation"; return "The object does not support aggregation";
break;
#endif #endif
#ifdef DSERR_NODRIVER #ifdef DSERR_NODRIVER
case DSERR_NODRIVER: case DSERR_NODRIVER:
str = "No sound driver is available for use, " return "No sound driver is available for use, "
"or the given GUID is not a valid DirectSound device ID"; "or the given GUID is not a valid DirectSound device ID";
break;
#endif #endif
#ifdef DSERR_NOINTERFACE #ifdef DSERR_NOINTERFACE
case DSERR_NOINTERFACE: case DSERR_NOINTERFACE:
str = "The requested COM interface is not available"; return "The requested COM interface is not available";
break;
#endif #endif
#ifdef DSERR_OBJECTNOTFOUND #ifdef DSERR_OBJECTNOTFOUND
case DSERR_OBJECTNOTFOUND: case DSERR_OBJECTNOTFOUND:
str = "The requested object was not found"; return "The requested object was not found";
break;
#endif #endif
#ifdef DSERR_OTHERAPPHASPRIO #ifdef DSERR_OTHERAPPHASPRIO
case DSERR_OTHERAPPHASPRIO: case DSERR_OTHERAPPHASPRIO:
str = "Another application has a higher priority level, " return "Another application has a higher priority level, "
"preventing this call from succeeding"; "preventing this call from succeeding";
break;
#endif #endif
#ifdef DSERR_OUTOFMEMORY #ifdef DSERR_OUTOFMEMORY
case DSERR_OUTOFMEMORY: case DSERR_OUTOFMEMORY:
str = "The DirectSound subsystem could not allocate " return "The DirectSound subsystem could not allocate "
"sufficient memory to complete the caller's request"; "sufficient memory to complete the caller's request";
break;
#endif #endif
#ifdef DSERR_PRIOLEVELNEEDED #ifdef DSERR_PRIOLEVELNEEDED
case DSERR_PRIOLEVELNEEDED: case DSERR_PRIOLEVELNEEDED:
str = "A cooperative level of DSSCL_PRIORITY or higher is required"; return "A cooperative level of DSSCL_PRIORITY or higher is required";
break;
#endif #endif
#ifdef DSERR_SENDLOOP #ifdef DSERR_SENDLOOP
case DSERR_SENDLOOP: case DSERR_SENDLOOP:
str = "A circular loop of send effects was detected"; return "A circular loop of send effects was detected";
break;
#endif #endif
#ifdef DSERR_UNINITIALIZED #ifdef DSERR_UNINITIALIZED
case DSERR_UNINITIALIZED: case DSERR_UNINITIALIZED:
str = "The Initialize method has not been called " return "The Initialize method has not been called "
"or has not been called successfully " "or has not been called successfully "
"before other methods were called"; "before other methods were called";
break;
#endif #endif
#ifdef DSERR_UNSUPPORTED #ifdef DSERR_UNSUPPORTED
case DSERR_UNSUPPORTED: case DSERR_UNSUPPORTED:
str = "The function called is not supported at this time"; return "The function called is not supported at this time";
break;
#endif #endif
default: default:
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr); return NULL;
return;
} }
}
static void dserror_set(Error **errp, HRESULT hr, const char *msg)
{
const char *str = dserror(hr);
if (str) {
error_setg(errp, "%s: %s", msg, str);
} else {
error_setg(errp, "%s: Unknown (HRESULT: 0x%lx)", msg, hr);
}
}
static void dsound_log_hresult(HRESULT hr)
{
const char *str = dserror(hr);
if (str) {
AUD_log (AUDIO_CAP, "Reason: %s\n", str); AUD_log (AUDIO_CAP, "Reason: %s\n", str);
} else {
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT: 0x%lx)\n", hr);
}
} }
static void G_GNUC_PRINTF (2, 3) dsound_logerr ( static void G_GNUC_PRINTF (2, 3) dsound_logerr (
@ -359,27 +351,6 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsound_unlock_out (dsb, p1, p2, blen1, blen2); dsound_unlock_out (dsb, p1, p2, blen1, blen2);
} }
static int dsound_set_cooperative_level(dsound *s)
{
HRESULT hr;
HWND hwnd;
hwnd = GetDesktopWindow();
hr = IDirectSound_SetCooperativeLevel (
s->dsound,
hwnd,
DSSCL_PRIORITY
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not set cooperative level for window %p\n",
hwnd);
return -1;
}
return 0;
}
static void dsound_enable_out(HWVoiceOut *hw, bool enable) static void dsound_enable_out(HWVoiceOut *hw, bool enable)
{ {
HRESULT hr; HRESULT hr;
@ -621,7 +592,6 @@ static void dsound_audio_fini (void *opaque)
static void *dsound_audio_init(Audiodev *dev, Error **errp) static void *dsound_audio_init(Audiodev *dev, Error **errp)
{ {
int err;
HRESULT hr; HRESULT hr;
dsound *s = g_new0(dsound, 1); dsound *s = g_new0(dsound, 1);
AudiodevDsoundOptions *dso; AudiodevDsoundOptions *dso;
@ -637,8 +607,8 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
hr = CoInitialize (NULL); hr = CoInitialize (NULL);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n"); dserror_set(errp, hr, "Could not initialize COM");
g_free(s); dsound_audio_fini(s);
return NULL; return NULL;
} }
@ -650,20 +620,15 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
(void **) &s->dsound (void **) &s->dsound
); );
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not create DirectSound instance\n"); dserror_set(errp, hr, "Could not create DirectSound instance");
g_free(s); dsound_audio_fini(s);
return NULL; return NULL;
} }
hr = IDirectSound_Initialize (s->dsound, NULL); hr = IDirectSound_Initialize (s->dsound, NULL);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize DirectSound\n"); dserror_set(errp, hr, "Could not initialize DirectSound");
dsound_audio_fini(s);
hr = IDirectSound_Release (s->dsound);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSound\n");
}
g_free(s);
return NULL; return NULL;
} }
@ -675,22 +640,25 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
(void **) &s->dsound_capture (void **) &s->dsound_capture
); );
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); dserror_set(errp, hr, "Could not create DirectSoundCapture instance");
} else { dsound_audio_fini(s);
return NULL;
}
hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); dserror_set(errp, hr, "Could not initialize DirectSoundCapture");
dsound_audio_fini(s);
return NULL;
}
hr = IDirectSoundCapture_Release (s->dsound_capture); hr = IDirectSound_SetCooperativeLevel (
s->dsound,
GetDesktopWindow(),
DSSCL_PRIORITY
);
if (FAILED(hr)) { if (FAILED(hr)) {
dsound_logerr (hr, "Could not release DirectSoundCapture\n"); dserror_set(errp, hr, "Could not set cooperative level");
}
s->dsound_capture = NULL;
}
}
err = dsound_set_cooperative_level(s);
if (err) {
dsound_audio_fini(s); dsound_audio_fini(s);
return NULL; return NULL;
} }
@ -717,7 +685,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
static struct audio_driver dsound_audio_driver = { static struct audio_driver dsound_audio_driver = {
.name = "dsound", .name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.init = dsound_audio_init, .init = dsound_audio_init,
.fini = dsound_audio_fini, .fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops, .pcm_ops = &dsound_pcm_ops,

View file

@ -26,7 +26,7 @@
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/atomic.h" #include "qemu/atomic.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "audio.h" #include "qemu/audio.h"
#define AUDIO_CAP "jack" #define AUDIO_CAP "jack"
#include "audio_int.h" #include "audio_int.h"
@ -672,7 +672,6 @@ static struct audio_pcm_ops jack_pcm_ops = {
static struct audio_driver jack_driver = { static struct audio_driver jack_driver = {
.name = "jack", .name = "jack",
.descr = "JACK Audio Connection Kit Client",
.init = qjack_init, .init = qjack_init,
.fini = qjack_fini, .fini = qjack_fini,
.pcm_ops = &jack_pcm_ops, .pcm_ops = &jack_pcm_ops,

View file

@ -1,12 +1,13 @@
system_ss.add([spice_headers, files('audio.c')])
system_ss.add(files( system_ss.add(files(
'audio-hmp-cmds.c', 'audio.c',
'mixeng.c', 'mixeng.c',
'noaudio.c', 'noaudio.c',
'wavaudio.c', 'wavaudio.c',
'wavcapture.c',
)) ))
# deprecated since v10.2, to be removed
system_ss.add(files('audio-hmp-cmds.c', 'wavcapture.c'))
system_ss.add(when: coreaudio, if_true: files('coreaudio.m')) system_ss.add(when: coreaudio, if_true: files('coreaudio.m'))
system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))

View file

@ -24,11 +24,13 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "qemu/error-report.h" #include "qemu/audio.h"
#include "audio.h"
#define AUDIO_CAP "mixeng" #define AUDIO_CAP "mixeng"
#include "audio_int.h" #include "audio_int.h"
#ifdef FLOAT_MIXENG
#include "qemu/error-report.h"
#endif
/* 8 bit */ /* 8 bit */
#define ENDIAN_CONVERSION natural #define ENDIAN_CONVERSION natural
@ -402,7 +404,7 @@ f_sample *mixeng_clip_float[2][2] = {
} }
}; };
void audio_sample_to_uint64(const void *samples, int pos, void audio_sample_to_uint64(const st_sample *sample, int pos,
uint64_t *left, uint64_t *right) uint64_t *left, uint64_t *right)
{ {
#ifdef FLOAT_MIXENG #ifdef FLOAT_MIXENG
@ -410,14 +412,13 @@ void audio_sample_to_uint64(const void *samples, int pos,
"Coreaudio and floating point samples are not supported by replay yet"); "Coreaudio and floating point samples are not supported by replay yet");
abort(); abort();
#else #else
const struct st_sample *sample = samples;
sample += pos; sample += pos;
*left = sample->l; *left = sample->l;
*right = sample->r; *right = sample->r;
#endif #endif
} }
void audio_sample_from_uint64(void *samples, int pos, void audio_sample_from_uint64(st_sample *sample, int pos,
uint64_t left, uint64_t right) uint64_t left, uint64_t right)
{ {
#ifdef FLOAT_MIXENG #ifdef FLOAT_MIXENG
@ -425,7 +426,6 @@ void audio_sample_from_uint64(void *samples, int pos,
"Coreaudio and floating point samples are not supported by replay yet"); "Coreaudio and floating point samples are not supported by replay yet");
abort(); abort();
#else #else
struct st_sample *sample = samples;
sample += pos; sample += pos;
sample->l = left; sample->l = left;
sample->r = right; sample->r = right;

View file

@ -33,7 +33,6 @@ struct st_sample { mixeng_real l; mixeng_real r; };
struct mixeng_volume { int mute; int64_t r; int64_t l; }; struct mixeng_volume { int mute; int64_t r; int64_t l; };
struct st_sample { int64_t l; int64_t r; }; struct st_sample { int64_t l; int64_t r; };
#endif #endif
typedef struct st_sample st_sample;
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);

View file

@ -23,10 +23,8 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "audio.h" #include "qemu/audio.h"
#include "qemu/timer.h"
#define AUDIO_CAP "noaudio" #define AUDIO_CAP "noaudio"
#include "audio_int.h" #include "audio_int.h"
@ -131,7 +129,6 @@ static struct audio_pcm_ops no_pcm_ops = {
static struct audio_driver no_audio_driver = { static struct audio_driver no_audio_driver = {
.name = "none", .name = "none",
.descr = "Timer based audio emulation",
.init = no_audio_init, .init = no_audio_init,
.fini = no_audio_fini, .fini = no_audio_fini,
.pcm_ops = &no_pcm_ops, .pcm_ops = &no_pcm_ops,

View file

@ -29,7 +29,7 @@
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "audio.h" #include "qemu/audio.h"
#include "trace.h" #include "trace.h"
#define AUDIO_CAP "oss" #define AUDIO_CAP "oss"
@ -107,13 +107,13 @@ static void oss_anal_close (int *fdp)
static void oss_helper_poll_out (void *opaque) static void oss_helper_poll_out (void *opaque)
{ {
AudioState *s = opaque; AudioBackend *s = opaque;
audio_run(s, "oss_poll_out"); audio_run(s, "oss_poll_out");
} }
static void oss_helper_poll_in (void *opaque) static void oss_helper_poll_in (void *opaque)
{ {
AudioState *s = opaque; AudioBackend *s = opaque;
audio_run(s, "oss_poll_in"); audio_run(s, "oss_poll_in");
} }
@ -131,7 +131,7 @@ static void oss_poll_in (HWVoiceIn *hw)
qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s); qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
} }
static int aud_to_ossfmt (AudioFormat fmt, int endianness) static int aud_to_ossfmt(AudioFormat fmt, bool big_endian)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUDIO_FORMAT_S8:
@ -141,18 +141,10 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
return AFMT_U8; return AFMT_U8;
case AUDIO_FORMAT_S16: case AUDIO_FORMAT_S16:
if (endianness) { return big_endian ? AFMT_S16_BE : AFMT_S16_LE;
return AFMT_S16_BE;
} else {
return AFMT_S16_LE;
}
case AUDIO_FORMAT_U16: case AUDIO_FORMAT_U16:
if (endianness) { return big_endian ? AFMT_U16_BE : AFMT_U16_LE;
return AFMT_U16_BE;
} else {
return AFMT_U16_LE;
}
default: default:
dolog ("Internal logic error: Bad audio format %d\n", fmt); dolog ("Internal logic error: Bad audio format %d\n", fmt);
@ -493,10 +485,8 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
{ {
OSSVoiceOut *oss = (OSSVoiceOut *) hw; OSSVoiceOut *oss = (OSSVoiceOut *) hw;
struct oss_params req, obt; struct oss_params req, obt;
int endianness;
int err; int err;
int fd; int fd;
AudioFormat effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; Audiodev *dev = drv_opaque;
AudiodevOssOptions *oopts = &dev->u.oss; AudiodevOssOptions *oopts = &dev->u.oss;
@ -511,7 +501,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1; return -1;
} }
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); err = oss_to_audfmt(obt.fmt, &obt_as.fmt, &obt_as.endianness);
if (err) { if (err) {
oss_anal_close (&fd); oss_anal_close (&fd);
return -1; return -1;
@ -519,8 +509,6 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
obt_as.freq = obt.freq; obt_as.freq = obt.freq;
obt_as.nchannels = obt.nchannels; obt_as.nchannels = obt.nchannels;
obt_as.fmt = effective_fmt;
obt_as.endianness = endianness;
audio_pcm_init_info (&hw->info, &obt_as); audio_pcm_init_info (&hw->info, &obt_as);
oss->nfrags = obt.nfrags; oss->nfrags = obt.nfrags;
@ -628,10 +616,8 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{ {
OSSVoiceIn *oss = (OSSVoiceIn *) hw; OSSVoiceIn *oss = (OSSVoiceIn *) hw;
struct oss_params req, obt; struct oss_params req, obt;
int endianness;
int err; int err;
int fd; int fd;
AudioFormat effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; Audiodev *dev = drv_opaque;
@ -644,7 +630,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
return -1; return -1;
} }
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); err = oss_to_audfmt(obt.fmt, &obt_as.fmt, &obt_as.endianness);
if (err) { if (err) {
oss_anal_close (&fd); oss_anal_close (&fd);
return -1; return -1;
@ -652,8 +638,6 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
obt_as.freq = obt.freq; obt_as.freq = obt.freq;
obt_as.nchannels = obt.nchannels; obt_as.nchannels = obt.nchannels;
obt_as.fmt = effective_fmt;
obt_as.endianness = endianness;
audio_pcm_init_info (&hw->info, &obt_as); audio_pcm_init_info (&hw->info, &obt_as);
oss->nfrags = obt.nfrags; oss->nfrags = obt.nfrags;
@ -779,7 +763,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
static struct audio_driver oss_audio_driver = { static struct audio_driver oss_audio_driver = {
.name = "oss", .name = "oss",
.descr = "OSS http://www.opensound.com",
.init = oss_audio_init, .init = oss_audio_init,
.fini = oss_audio_fini, .fini = oss_audio_fini,
.pcm_ops = &oss_pcm_ops, .pcm_ops = &oss_pcm_ops,

View file

@ -2,7 +2,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "audio.h" #include "qemu/audio.h"
#include "qapi/error.h" #include "qapi/error.h"
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
@ -316,7 +316,7 @@ unlock_and_fail:
return 0; return 0;
} }
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) static pa_sample_format_t audfmt_to_pa(AudioFormat afmt, bool big_endian)
{ {
int format; int format;
@ -327,14 +327,14 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
break; break;
case AUDIO_FORMAT_S16: case AUDIO_FORMAT_S16:
case AUDIO_FORMAT_U16: case AUDIO_FORMAT_U16:
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; format = big_endian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
break; break;
case AUDIO_FORMAT_S32: case AUDIO_FORMAT_S32:
case AUDIO_FORMAT_U32: case AUDIO_FORMAT_U32:
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; format = big_endian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
break; break;
case AUDIO_FORMAT_F32: case AUDIO_FORMAT_F32:
format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE; format = big_endian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
break; break;
default: default:
dolog ("Internal logic error: Bad audio format %d\n", afmt); dolog ("Internal logic error: Bad audio format %d\n", afmt);
@ -747,14 +747,13 @@ static void qpa_volume_in(HWVoiceIn *hw, Volume *vol)
pa_threaded_mainloop_unlock(c->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
} }
static int qpa_validate_per_direction_opts(Audiodev *dev, static void qpa_validate_per_direction_opts(Audiodev *dev,
AudiodevPaPerDirectionOptions *pdo) AudiodevPaPerDirectionOptions *pdo)
{ {
if (!pdo->has_latency) { if (!pdo->has_latency) {
pdo->has_latency = true; pdo->has_latency = true;
pdo->latency = 46440; pdo->latency = 46440;
} }
return 1;
} }
/* common */ /* common */
@ -844,12 +843,8 @@ static void *qpa_audio_init(Audiodev *dev, Error **errp)
} }
} }
if (!qpa_validate_per_direction_opts(dev, popts->in)) { qpa_validate_per_direction_opts(dev, popts->in);
return NULL; qpa_validate_per_direction_opts(dev, popts->out);
}
if (!qpa_validate_per_direction_opts(dev, popts->out)) {
return NULL;
}
g = g_new0(paaudio, 1); g = g_new0(paaudio, 1);
server = popts->server; server = popts->server;
@ -927,7 +922,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
static struct audio_driver pa_audio_driver = { static struct audio_driver pa_audio_driver = {
.name = "pa", .name = "pa",
.descr = "http://www.pulseaudio.org/",
.init = qpa_audio_init, .init = qpa_audio_init,
.fini = qpa_audio_fini, .fini = qpa_audio_fini,
.pcm_ops = &qpa_pcm_ops, .pcm_ops = &qpa_pcm_ops,

View file

@ -10,7 +10,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "audio.h" #include "qemu/audio.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/error.h" #include "qapi/error.h"
#include <spa/param/audio/format-utils.h> #include <spa/param/audio/format-utils.h>
@ -324,7 +324,7 @@ done_unlock:
} }
static int static int
audfmt_to_pw(AudioFormat fmt, int endianness) audfmt_to_pw(AudioFormat fmt, bool big_endian)
{ {
int format; int format;
@ -336,19 +336,19 @@ audfmt_to_pw(AudioFormat fmt, int endianness)
format = SPA_AUDIO_FORMAT_U8; format = SPA_AUDIO_FORMAT_U8;
break; break;
case AUDIO_FORMAT_S16: case AUDIO_FORMAT_S16:
format = endianness ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE; format = big_endian ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE;
break; break;
case AUDIO_FORMAT_U16: case AUDIO_FORMAT_U16:
format = endianness ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE; format = big_endian ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE;
break; break;
case AUDIO_FORMAT_S32: case AUDIO_FORMAT_S32:
format = endianness ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE; format = big_endian ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE;
break; break;
case AUDIO_FORMAT_U32: case AUDIO_FORMAT_U32:
format = endianness ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE; format = big_endian ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE;
break; break;
case AUDIO_FORMAT_F32: case AUDIO_FORMAT_F32:
format = endianness ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE; format = big_endian ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE;
break; break;
default: default:
dolog("Internal logic error: Bad audio format %d\n", fmt); dolog("Internal logic error: Bad audio format %d\n", fmt);
@ -838,7 +838,6 @@ static struct audio_pcm_ops qpw_pcm_ops = {
static struct audio_driver pw_audio_driver = { static struct audio_driver pw_audio_driver = {
.name = "pipewire", .name = "pipewire",
.descr = "http://www.pipewire.org/",
.init = qpw_audio_init, .init = qpw_audio_init,
.fini = qpw_audio_fini, .fini = qpw_audio_fini,
.pcm_ops = &qpw_pcm_ops, .pcm_ops = &qpw_pcm_ops,

View file

@ -27,7 +27,7 @@
#include <SDL_thread.h> #include <SDL_thread.h>
#include "qemu/module.h" #include "qemu/module.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "audio.h" #include "qemu/audio.h"
#ifndef _WIN32 #ifndef _WIN32
#ifdef __sun__ #ifdef __sun__
@ -338,9 +338,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
{ {
SDLVoiceOut *sdl = (SDLVoiceOut *)hw; SDLVoiceOut *sdl = (SDLVoiceOut *)hw;
SDL_AudioSpec req, obt; SDL_AudioSpec req, obt;
int endianness;
int err; int err;
AudioFormat effective_fmt;
Audiodev *dev = drv_opaque; Audiodev *dev = drv_opaque;
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out; AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out;
struct audsettings obt_as; struct audsettings obt_as;
@ -360,7 +358,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1; return -1;
} }
err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); err = sdl_to_audfmt(obt.format, &obt_as.fmt, &obt_as.endianness);
if (err) { if (err) {
sdl_close_out(sdl); sdl_close_out(sdl);
return -1; return -1;
@ -368,8 +366,6 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
obt_as.freq = obt.freq; obt_as.freq = obt.freq;
obt_as.nchannels = obt.channels; obt_as.nchannels = obt.channels;
obt_as.fmt = effective_fmt;
obt_as.endianness = endianness;
audio_pcm_init_info (&hw->info, &obt_as); audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
@ -398,9 +394,7 @@ static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
{ {
SDLVoiceIn *sdl = (SDLVoiceIn *)hw; SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
SDL_AudioSpec req, obt; SDL_AudioSpec req, obt;
int endianness;
int err; int err;
AudioFormat effective_fmt;
Audiodev *dev = drv_opaque; Audiodev *dev = drv_opaque;
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in; AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in;
struct audsettings obt_as; struct audsettings obt_as;
@ -420,7 +414,7 @@ static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
return -1; return -1;
} }
err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); err = sdl_to_audfmt(obt.format, &obt_as.fmt, &obt_as.endianness);
if (err) { if (err) {
sdl_close_in(sdl); sdl_close_in(sdl);
return -1; return -1;
@ -428,8 +422,6 @@ static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
obt_as.freq = obt.freq; obt_as.freq = obt.freq;
obt_as.nchannels = obt.channels; obt_as.nchannels = obt.channels;
obt_as.fmt = effective_fmt;
obt_as.endianness = endianness;
audio_pcm_init_info(&hw->info, &obt_as); audio_pcm_init_info(&hw->info, &obt_as);
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
@ -490,7 +482,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
static struct audio_driver sdl_audio_driver = { static struct audio_driver sdl_audio_driver = {
.name = "sdl", .name = "sdl",
.descr = "SDL http://www.libsdl.org",
.init = sdl_audio_init, .init = sdl_audio_init,
.fini = sdl_audio_fini, .fini = sdl_audio_fini,
.pcm_ops = &sdl_pcm_ops, .pcm_ops = &sdl_pcm_ops,

View file

@ -18,7 +18,7 @@
#include <poll.h> #include <poll.h>
#include <sndio.h> #include <sndio.h>
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "audio.h" #include "qemu/audio.h"
#include "trace.h" #include "trace.h"
#define AUDIO_CAP "sndio" #define AUDIO_CAP "sndio"
@ -546,7 +546,6 @@ static struct audio_pcm_ops sndio_pcm_ops = {
static struct audio_driver sndio_audio_driver = { static struct audio_driver sndio_audio_driver = {
.name = "sndio", .name = "sndio",
.descr = "sndio https://sndio.org",
.init = sndio_audio_init, .init = sndio_audio_init,
.fini = sndio_audio_fini, .fini = sndio_audio_fini,
.pcm_ops = &sndio_pcm_ops, .pcm_ops = &sndio_pcm_ops,

View file

@ -26,7 +26,7 @@
#include "ui/qemu-spice.h" #include "ui/qemu-spice.h"
#define AUDIO_CAP "spice" #define AUDIO_CAP "spice"
#include "audio.h" #include "qemu/audio.h"
#include "audio_int.h" #include "audio_int.h"
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
@ -102,7 +102,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
#endif #endif
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
settings.fmt = AUDIO_FORMAT_S16; settings.fmt = AUDIO_FORMAT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS; settings.endianness = HOST_BIG_ENDIAN;
audio_pcm_init_info (&hw->info, &settings); audio_pcm_init_info (&hw->info, &settings);
hw->samples = LINE_OUT_SAMPLES; hw->samples = LINE_OUT_SAMPLES;
@ -218,7 +218,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
#endif #endif
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
settings.fmt = AUDIO_FORMAT_S16; settings.fmt = AUDIO_FORMAT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS; settings.endianness = HOST_BIG_ENDIAN;
audio_pcm_init_info (&hw->info, &settings); audio_pcm_init_info (&hw->info, &settings);
hw->samples = LINE_IN_SAMPLES; hw->samples = LINE_IN_SAMPLES;
@ -316,7 +316,6 @@ static struct audio_pcm_ops audio_callbacks = {
static struct audio_driver spice_audio_driver = { static struct audio_driver spice_audio_driver = {
.name = "spice", .name = "spice",
.descr = "spice audio driver",
.init = spice_audio_init, .init = spice_audio_init,
.fini = spice_audio_fini, .fini = spice_audio_fini,
.pcm_ops = &audio_callbacks, .pcm_ops = &audio_callbacks,

View file

@ -23,11 +23,8 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/timer.h" #include "qemu/audio.h"
#include "qapi/opts-visitor.h"
#include "audio.h"
#define AUDIO_CAP "wav" #define AUDIO_CAP "wav"
#include "audio_int.h" #include "audio_int.h"
@ -208,7 +205,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
static struct audio_driver wav_audio_driver = { static struct audio_driver wav_audio_driver = {
.name = "wav", .name = "wav",
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
.init = wav_audio_init, .init = wav_audio_init,
.fini = wav_audio_fini, .fini = wav_audio_fini,
.pcm_ops = &wav_pcm_ops, .pcm_ops = &wav_pcm_ops,

View file

@ -1,8 +1,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/qemu-print.h" #include "qemu/qemu-print.h"
#include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "audio.h" #include "audio_int.h"
typedef struct { typedef struct {
FILE *f; FILE *f;
@ -104,7 +103,7 @@ static struct capture_ops wav_capture_ops = {
.info = wav_capture_info .info = wav_capture_info
}; };
int wav_start_capture(AudioState *state, CaptureState *s, const char *path, int wav_start_capture(AudioBackend *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels) int freq, int bits, int nchannels)
{ {
WAVState *wav; WAVState *wav;

View file

@ -53,6 +53,8 @@ typedef struct CryptoDevBackendBuiltinSession {
#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
#define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN 64 #define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN 64
/* demonstration purposes only, use a limited size to avoid QEMU OOM */
#define CRYPTODEV_BUITLIN_MAX_REQUEST_SIZE (1024 * 1024)
struct CryptoDevBackendBuiltin { struct CryptoDevBackendBuiltin {
CryptoDevBackend parent_obj; CryptoDevBackend parent_obj;
@ -98,12 +100,7 @@ static void cryptodev_builtin_init(
1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC; 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC;
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
/* backend->conf.max_size = CRYPTODEV_BUITLIN_MAX_REQUEST_SIZE;
* Set the Maximum length of crypto request.
* Why this value? Just avoid to overflow when
* memory allocation for each crypto request.
*/
backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN; backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN; backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
cryptodev_builtin_init_akcipher(backend); cryptodev_builtin_init_akcipher(backend);

View file

@ -68,7 +68,6 @@ typedef struct CryptoDevBackendLKCFSession {
QCryptoAkCipherOptions akcipher_opts; QCryptoAkCipherOptions akcipher_opts;
} CryptoDevBackendLKCFSession; } CryptoDevBackendLKCFSession;
typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF;
typedef struct CryptoDevLKCFTask CryptoDevLKCFTask; typedef struct CryptoDevLKCFTask CryptoDevLKCFTask;
struct CryptoDevLKCFTask { struct CryptoDevLKCFTask {
CryptoDevBackendLKCFSession *sess; CryptoDevBackendLKCFSession *sess;

View file

@ -46,7 +46,7 @@ struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj; CryptoDevBackend parent_obj;
VhostUserState vhost_user; VhostUserState vhost_user;
CharBackend chr; CharFrontend chr;
char *chr_name; char *chr_name;
bool opened; bool opened;
CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM]; CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM];

View file

@ -54,6 +54,7 @@ have_fd:
/* Let's do the same as memory-backend-ram,share=on would do. */ /* Let's do the same as memory-backend-ram,share=on would do. */
ram_flags = RAM_SHARED; ram_flags = RAM_SHARED;
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0;
return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
backend_name, backend->size, backend_name, backend->size,

View file

@ -12,7 +12,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "system/igvm-cfg.h" #include "system/igvm-cfg.h"
#include "igvm.h" #include "system/igvm.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
static char *get_igvm(Object *obj, Error **errp) static char *get_igvm(Object *obj, Error **errp)

View file

@ -11,8 +11,9 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "igvm.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/target-info-qapi.h"
#include "system/igvm.h"
#include "system/memory.h" #include "system/memory.h"
#include "system/address-spaces.h" #include "system/address-spaces.h"
#include "hw/core/cpu.h" #include "hw/core/cpu.h"
@ -431,18 +432,6 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
return 0; return 0;
} }
/*
* A confidential guest support object must be provided for setting
* a VP context.
*/
if (!ctx->cgs) {
error_setg(
errp,
"A VP context is present in the IGVM file but is not supported "
"by the current system.");
return -1;
}
data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
ctx->current_header_index); ctx->current_header_index);
if (data_handle < 0) { if (data_handle < 0) {
@ -452,9 +441,21 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
} }
data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle); data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
if (ctx->cgs) {
result = ctx->cgsc->set_guest_state( result = ctx->cgsc->set_guest_state(
vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle), vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
} else if (target_arch() == SYS_EMU_TARGET_X86_64) {
result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp);
} else {
error_setg(
errp,
"A VP context is present in the IGVM file but is not supported "
"by the current system.");
result = -1;
}
igvm_free_buffer(ctx->file, data_handle); igvm_free_buffer(ctx->file, data_handle);
if (result < 0) { if (result < 0) {
return result; return result;
@ -543,6 +544,8 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
Error **errp) Error **errp)
{ {
const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry,
Error **errp) = NULL;
QIgvmParameterData *param_entry; QIgvmParameterData *param_entry;
int max_entry_count; int max_entry_count;
int entry = 0; int entry = 0;
@ -550,7 +553,13 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
ConfidentialGuestMemoryMapEntry cgmm_entry; ConfidentialGuestMemoryMapEntry cgmm_entry;
int retval = 0; int retval = 0;
if (!ctx->cgs) { if (ctx->cgs && ctx->cgsc->get_mem_map_entry) {
get_mem_map_entry = ctx->cgsc->get_mem_map_entry;
} else if (target_arch() == SYS_EMU_TARGET_X86_64) {
get_mem_map_entry = qigvm_x86_get_mem_map_entry;
} else {
error_setg(errp, error_setg(errp,
"IGVM file contains a memory map but this is not supported " "IGVM file contains a memory map but this is not supported "
"by the current system."); "by the current system.");
@ -565,9 +574,9 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY); param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data; mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp); retval = get_mem_map_entry(entry, &cgmm_entry, errp);
while (retval == 0) { while (retval == 0) {
if (entry > max_entry_count) { if (entry >= max_entry_count) {
error_setg( error_setg(
errp, errp,
"IGVM: guest memory map size exceeds parameter area defined in IGVM file"); "IGVM: guest memory map size exceeds parameter area defined in IGVM file");
@ -598,8 +607,7 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
break; break;
} }
retval = retval = get_mem_map_entry(++entry, &cgmm_entry, errp);
ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
} }
if (retval < 0) { if (retval < 0) {
return retval; return retval;

View file

@ -197,7 +197,7 @@ void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id)
} }
int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
ram_addr_t size, void *vaddr, bool readonly) uint64_t size, void *vaddr, bool readonly)
{ {
int ret, fd = be->fd; int ret, fd = be->fd;
struct iommu_ioas_map map = { struct iommu_ioas_map map = {
@ -230,7 +230,7 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
} }
int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id,
hwaddr iova, ram_addr_t size, hwaddr iova, uint64_t size,
int mfd, unsigned long start, bool readonly) int mfd, unsigned long start, bool readonly)
{ {
int ret, fd = be->fd; int ret, fd = be->fd;
@ -268,7 +268,7 @@ int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id,
} }
int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
hwaddr iova, ram_addr_t size) hwaddr iova, uint64_t size)
{ {
int ret, fd = be->fd; int ret, fd = be->fd;
struct iommu_ioas_unmap unmap = { struct iommu_ioas_unmap unmap = {

View file

@ -24,7 +24,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(RngEgd, RNG_EGD)
struct RngEgd { struct RngEgd {
RngBackend parent; RngBackend parent;
CharBackend chr; CharFrontend chr;
char *chr_name; char *chr_name;
}; };

View file

@ -13,6 +13,9 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "system/spdm-socket.h" #include "system/spdm-socket.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/core/qdev-prop-internal.h"
static bool read_bytes(const int socket, uint8_t *buffer, static bool read_bytes(const int socket, uint8_t *buffer,
size_t number_of_bytes) size_t number_of_bytes)
@ -184,29 +187,61 @@ int spdm_socket_connect(uint16_t port, Error **errp)
return client_socket; return client_socket;
} }
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type, static bool spdm_socket_command_valid(uint32_t command)
void *req, uint32_t req_len, {
switch (command) {
case SPDM_SOCKET_COMMAND_NORMAL:
case SPDM_SOCKET_STORAGE_CMD_IF_SEND:
case SPDM_SOCKET_STORAGE_CMD_IF_RECV:
case SOCKET_SPDM_STORAGE_ACK_STATUS:
case SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE:
case SPDM_SOCKET_COMMAND_CONTINUE:
case SPDM_SOCKET_COMMAND_SHUTDOWN:
case SPDM_SOCKET_COMMAND_UNKOWN:
case SPDM_SOCKET_COMMAND_TEST:
return true;
default:
return false;
}
}
uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
void *rsp, uint32_t rsp_len) void *rsp, uint32_t rsp_len)
{ {
uint32_t command; uint32_t command;
bool result; bool result;
result = send_platform_data(socket, transport_type,
SPDM_SOCKET_COMMAND_NORMAL,
req, req_len);
if (!result) {
return 0;
}
result = receive_platform_data(socket, transport_type, &command, result = receive_platform_data(socket, transport_type, &command,
(uint8_t *)rsp, &rsp_len); (uint8_t *)rsp, &rsp_len);
/* we may have received some data, but check if the command is valid */
if (!result || !spdm_socket_command_valid(command)) {
return 0;
}
return rsp_len;
}
bool spdm_socket_send(const int socket, uint32_t socket_cmd,
uint32_t transport_type, void *req, uint32_t req_len)
{
return send_platform_data(socket, transport_type, socket_cmd, req,
req_len);
}
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
void *req, uint32_t req_len,
void *rsp, uint32_t rsp_len)
{
bool result;
result = spdm_socket_send(socket, SPDM_SOCKET_COMMAND_NORMAL,
transport_type, req, req_len);
if (!result) { if (!result) {
return 0; return 0;
} }
assert(command != 0); return spdm_socket_receive(socket, transport_type, rsp, rsp_len);
return rsp_len;
} }
void spdm_socket_close(const int socket, uint32_t transport_type) void spdm_socket_close(const int socket, uint32_t transport_type)
@ -214,3 +249,23 @@ void spdm_socket_close(const int socket, uint32_t transport_type)
send_platform_data(socket, transport_type, send_platform_data(socket, transport_type,
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0); SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
} }
const QEnumLookup SpdmTransport_lookup = {
.array = (const char *const[]) {
[SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC] = "unspecified",
[SPDM_SOCKET_TRANSPORT_TYPE_MCTP] = "mctp",
[SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE] = "doe",
[SPDM_SOCKET_TRANSPORT_TYPE_SCSI] = "scsi",
[SPDM_SOCKET_TRANSPORT_TYPE_NVME] = "nvme",
},
.size = SPDM_SOCKET_TRANSPORT_TYPE_MAX
};
const PropertyInfo qdev_prop_spdm_trans = {
.type = "SpdmTransportType",
.description = "Spdm Transport, doe/nvme/mctp/scsi/unspecified",
.enum_table = &SpdmTransport_lookup,
.get = qdev_propinfo_get_enum,
.set = qdev_propinfo_set_enum,
.set_default_value = qdev_propinfo_set_default_value_enum,
};

View file

@ -69,7 +69,7 @@ struct TPMEmulator {
TPMBackend parent; TPMBackend parent;
TPMEmulatorOptions *options; TPMEmulatorOptions *options;
CharBackend ctrl_chr; CharFrontend ctrl_chr;
QIOChannel *data_ioc; QIOChannel *data_ioc;
TPMVersion tpm_version; TPMVersion tpm_version;
uint32_t caps; /* capabilities of the TPM */ uint32_t caps; /* capabilities of the TPM */
@ -126,7 +126,7 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
size_t msg_len_in, size_t msg_len_out_err, size_t msg_len_in, size_t msg_len_out_err,
size_t msg_len_out_total) size_t msg_len_out_total)
{ {
CharBackend *dev = &tpm->ctrl_chr; CharFrontend *dev = &tpm->ctrl_chr;
uint32_t cmd_no = cpu_to_be32(cmd); uint32_t cmd_no = cpu_to_be32(cmd);
ssize_t n = sizeof(uint32_t) + msg_len_in; ssize_t n = sizeof(uint32_t) + msg_len_in;
ptm_res res; ptm_res res;
@ -308,21 +308,21 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
return 0; return 0;
} }
static int tpm_emulator_stop_tpm(TPMBackend *tb) static int tpm_emulator_stop_tpm(TPMBackend *tb, Error **errp)
{ {
TPMEmulator *tpm_emu = TPM_EMULATOR(tb); TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_res res; ptm_res res;
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0,
sizeof(ptm_res), sizeof(res)) < 0) { sizeof(ptm_res), sizeof(res)) < 0) {
error_report("tpm-emulator: Could not stop TPM: %s", error_setg(errp, "tpm-emulator: Could not stop TPM: %s",
strerror(errno)); strerror(errno));
return -1; return -1;
} }
res = be32_to_cpu(res); res = be32_to_cpu(res);
if (res) { if (res) {
error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res, error_setg(errp, "tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res,
tpm_emulator_strerror(res)); tpm_emulator_strerror(res));
return -1; return -1;
} }
@ -362,12 +362,13 @@ static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu)
static int tpm_emulator_set_buffer_size(TPMBackend *tb, static int tpm_emulator_set_buffer_size(TPMBackend *tb,
size_t wanted_size, size_t wanted_size,
size_t *actual_size) size_t *actual_size,
Error **errp)
{ {
TPMEmulator *tpm_emu = TPM_EMULATOR(tb); TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_setbuffersize psbs; ptm_setbuffersize psbs;
if (tpm_emulator_stop_tpm(tb) < 0) { if (tpm_emulator_stop_tpm(tb, errp) < 0) {
return -1; return -1;
} }
@ -376,14 +377,15 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result), sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result),
sizeof(psbs.u.resp)) < 0) { sizeof(psbs.u.resp)) < 0) {
error_report("tpm-emulator: Could not set buffer size: %s", error_setg(errp, "tpm-emulator: Could not set buffer size: %s",
strerror(errno)); strerror(errno));
return -1; return -1;
} }
psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
if (psbs.u.resp.tpm_result != 0) { if (psbs.u.resp.tpm_result != 0) {
error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s", error_setg(errp,
"tpm-emulator: TPM result for set buffer size : 0x%x %s",
psbs.u.resp.tpm_result, psbs.u.resp.tpm_result,
tpm_emulator_strerror(psbs.u.resp.tpm_result)); tpm_emulator_strerror(psbs.u.resp.tpm_result));
return -1; return -1;
@ -402,7 +404,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
} }
static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
bool is_resume) bool is_resume, Error **errp)
{ {
TPMEmulator *tpm_emu = TPM_EMULATOR(tb); TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_init init = { ptm_init init = {
@ -413,7 +415,7 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize);
if (buffersize != 0 && if (buffersize != 0 &&
tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { tpm_emulator_set_buffer_size(tb, buffersize, NULL, errp) < 0) {
goto err_exit; goto err_exit;
} }
@ -424,14 +426,14 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
sizeof(init.u.resp.tpm_result), sizeof(init.u.resp.tpm_result),
sizeof(init)) < 0) { sizeof(init)) < 0) {
error_report("tpm-emulator: could not send INIT: %s", error_setg(errp, "tpm-emulator: could not send INIT: %s",
strerror(errno)); strerror(errno));
goto err_exit; goto err_exit;
} }
res = be32_to_cpu(init.u.resp.tpm_result); res = be32_to_cpu(init.u.resp.tpm_result);
if (res) { if (res) {
error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res, error_setg(errp, "tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res,
tpm_emulator_strerror(res)); tpm_emulator_strerror(res));
goto err_exit; goto err_exit;
} }
@ -441,18 +443,31 @@ err_exit:
return -1; return -1;
} }
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) static int do_tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize,
Error **errp)
{ {
/* TPM startup will be done from post_load hook */ /* TPM startup will be done from post_load hook */
if (runstate_check(RUN_STATE_INMIGRATE)) { if (runstate_check(RUN_STATE_INMIGRATE)) {
if (buffersize != 0) { if (buffersize != 0) {
return tpm_emulator_set_buffer_size(tb, buffersize, NULL); return tpm_emulator_set_buffer_size(tb, buffersize, NULL, errp);
} }
return 0; return 0;
} }
return tpm_emulator_startup_tpm_resume(tb, buffersize, false); return tpm_emulator_startup_tpm_resume(tb, buffersize, false, errp);
}
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
{
Error *local_err = NULL;
int ret = do_tpm_emulator_startup_tpm(tb, buffersize, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
return ret;
} }
static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
@ -546,7 +561,7 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
{ {
size_t actual_size; size_t actual_size;
if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { if (tpm_emulator_set_buffer_size(tb, 0, &actual_size, NULL) < 0) {
return 4096; return 4096;
} }
@ -819,7 +834,8 @@ static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu)
static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
uint32_t type, uint32_t type,
TPMSizedBuffer *tsb, TPMSizedBuffer *tsb,
uint32_t flags) uint32_t flags,
Error **errp)
{ {
ssize_t n; ssize_t n;
ptm_setstate pss; ptm_setstate pss;
@ -838,15 +854,16 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
/* write the header only */ /* write the header only */
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
offsetof(ptm_setstate, u.req.data), 0, 0) < 0) { offsetof(ptm_setstate, u.req.data), 0, 0) < 0) {
error_report("tpm-emulator: could not set state blob type %d : %s", error_setg_errno(errp, errno,
type, strerror(errno)); "tpm-emulator: could not set state blob type %d",
type);
return -1; return -1;
} }
/* now the body */ /* now the body */
n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size);
if (n != tsb->size) { if (n != tsb->size) {
error_report("tpm-emulator: Writing the stateblob (type %d) " error_setg(errp, "tpm-emulator: Writing the stateblob (type %d) "
"failed; could not write %u bytes, but only %zd", "failed; could not write %u bytes, but only %zd",
type, tsb->size, n); type, tsb->size, n);
return -1; return -1;
@ -856,16 +873,16 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr,
(uint8_t *)&pss, sizeof(pss.u.resp)); (uint8_t *)&pss, sizeof(pss.u.resp));
if (n != sizeof(pss.u.resp)) { if (n != sizeof(pss.u.resp)) {
error_report("tpm-emulator: Reading response from writing stateblob " error_setg(errp, "tpm-emulator: Reading response from writing "
"(type %d) failed; expected %zu bytes, got %zd", type, "stateblob (type %d) failed; expected %zu bytes, "
sizeof(pss.u.resp), n); "got %zd", type, sizeof(pss.u.resp), n);
return -1; return -1;
} }
tpm_result = be32_to_cpu(pss.u.resp.tpm_result); tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
if (tpm_result != 0) { if (tpm_result != 0) {
error_report("tpm-emulator: Setting the stateblob (type %d) failed " error_setg(errp, "tpm-emulator: Setting the stateblob (type %d) "
"with a TPM error 0x%x %s", type, tpm_result, "failed with a TPM error 0x%x %s", type, tpm_result,
tpm_emulator_strerror(tpm_result)); tpm_emulator_strerror(tpm_result));
return -1; return -1;
} }
@ -880,27 +897,27 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
* *
* Returns a negative errno code in case of error. * Returns a negative errno code in case of error.
*/ */
static int tpm_emulator_set_state_blobs(TPMBackend *tb) static int tpm_emulator_set_state_blobs(TPMBackend *tb, Error **errp)
{ {
TPMEmulator *tpm_emu = TPM_EMULATOR(tb); TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
trace_tpm_emulator_set_state_blobs(); trace_tpm_emulator_set_state_blobs();
if (tpm_emulator_stop_tpm(tb) < 0) { if (tpm_emulator_stop_tpm(tb, errp) < 0) {
trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); trace_tpm_emulator_set_state_blobs_error("Could not stop TPM");
return -EIO; return -EIO;
} }
if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
&state_blobs->permanent, &state_blobs->permanent,
state_blobs->permanent_flags) < 0 || state_blobs->permanent_flags, errp) < 0 ||
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
&state_blobs->volatil, &state_blobs->volatil,
state_blobs->volatil_flags) < 0 || state_blobs->volatil_flags, errp) < 0 ||
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
&state_blobs->savestate, &state_blobs->savestate,
state_blobs->savestate_flags) < 0) { state_blobs->savestate_flags, errp) < 0) {
return -EIO; return -EIO;
} }
@ -945,31 +962,29 @@ static void tpm_emulator_vm_state_change(void *opaque, bool running,
/* /*
* Load the TPM state blobs into the TPM. * Load the TPM state blobs into the TPM.
*
* Returns negative errno codes in case of error.
*/ */
static int tpm_emulator_post_load(void *opaque, int version_id) static bool tpm_emulator_post_load(void *opaque, int version_id, Error **errp)
{ {
TPMBackend *tb = opaque; TPMBackend *tb = opaque;
int ret; int ret;
ret = tpm_emulator_set_state_blobs(tb); ret = tpm_emulator_set_state_blobs(tb, errp);
if (ret < 0) { if (ret < 0) {
return ret; return false;
} }
if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { if (tpm_emulator_startup_tpm_resume(tb, 0, true, errp) < 0) {
return -EIO; return false;
} }
return 0; return true;
} }
static const VMStateDescription vmstate_tpm_emulator = { static const VMStateDescription vmstate_tpm_emulator = {
.name = "tpm-emulator", .name = "tpm-emulator",
.version_id = 0, .version_id = 0,
.pre_save = tpm_emulator_pre_save, .pre_save = tpm_emulator_pre_save,
.post_load = tpm_emulator_post_load, .post_load_errp = tpm_emulator_post_load,
.fields = (const VMStateField[]) { .fields = (const VMStateField[]) {
VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator),
VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator),

View file

@ -211,7 +211,7 @@ static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
{ {
int fd = -1; int fd = -1;
char *dev; const char *dev;
char path[PATH_MAX]; char path[PATH_MAX];
if (tpm_pt->options->cancel_path) { if (tpm_pt->options->cancel_path) {

57
block.c
View file

@ -606,12 +606,13 @@ create_file_fallback_zero_first_sector(BlockBackend *blk,
int64_t current_size, int64_t current_size,
Error **errp) Error **errp)
{ {
uint32_t alignment = blk_get_pwrite_zeroes_alignment(blk);
int64_t bytes_to_clear; int64_t bytes_to_clear;
int ret; int ret;
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); bytes_to_clear = MIN(current_size, MAX(BDRV_SECTOR_SIZE, alignment));
if (bytes_to_clear) { if (bytes_to_clear) {
ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP); ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
if (ret < 0) { if (ret < 0) {
@ -693,7 +694,7 @@ out:
} }
int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts, int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
Error **errp) bool allow_protocol_prefix, Error **errp)
{ {
QemuOpts *protocol_opts; QemuOpts *protocol_opts;
BlockDriver *drv; BlockDriver *drv;
@ -702,7 +703,7 @@ int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
drv = bdrv_find_protocol(filename, true, errp); drv = bdrv_find_protocol(filename, allow_protocol_prefix, errp);
if (drv == NULL) { if (drv == NULL) {
return -ENOENT; return -ENOENT;
} }
@ -1497,6 +1498,17 @@ static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child)
} }
} }
static void coroutine_fn GRAPH_RDLOCK bdrv_child_cb_resize(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
if (child->role & BDRV_CHILD_FILTERED) {
/* Best effort, ignore errors. */
bdrv_co_refresh_total_sectors(bs, bs->total_sectors);
bdrv_co_parent_cb_resize(bs);
}
}
static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
const char *filename, const char *filename,
bool backing_mask_protocol, bool backing_mask_protocol,
@ -1529,6 +1541,7 @@ const BdrvChildClass child_of_bds = {
.detach = bdrv_child_cb_detach, .detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate, .inactivate = bdrv_child_cb_inactivate,
.change_aio_ctx = bdrv_child_cb_change_aio_ctx, .change_aio_ctx = bdrv_child_cb_change_aio_ctx,
.resize = bdrv_child_cb_resize,
.update_filename = bdrv_child_cb_update_filename, .update_filename = bdrv_child_cb_update_filename,
.get_parent_aio_context = child_of_bds_get_parent_aio_context, .get_parent_aio_context = child_of_bds_get_parent_aio_context,
}; };
@ -5386,17 +5399,13 @@ bdrv_replace_node_noperm(BlockDriverState *from,
* *
* With auto_skip=false the error is returned if from has a parent which should * With auto_skip=false the error is returned if from has a parent which should
* not be updated. * not be updated.
*
* With @detach_subchain=true @to must be in a backing chain of @from. In this
* case backing link of the cow-parent of @to is removed.
*/ */
static int GRAPH_WRLOCK static int GRAPH_WRLOCK
bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
bool auto_skip, bool detach_subchain, Error **errp) bool auto_skip, Error **errp)
{ {
Transaction *tran = tran_new(); Transaction *tran = tran_new();
g_autoptr(GSList) refresh_list = NULL; g_autoptr(GSList) refresh_list = NULL;
BlockDriverState *to_cow_parent = NULL;
int ret; int ret;
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
@ -5405,17 +5414,6 @@ bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
assert(to->quiesce_counter); assert(to->quiesce_counter);
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
if (detach_subchain) {
assert(bdrv_chain_contains(from, to));
assert(from != to);
for (to_cow_parent = from;
bdrv_filter_or_cow_bs(to_cow_parent) != to;
to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent))
{
;
}
}
/* /*
* Do the replacement without permission update. * Do the replacement without permission update.
* Replacement may influence the permissions, we should calculate new * Replacement may influence the permissions, we should calculate new
@ -5427,11 +5425,6 @@ bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
goto out; goto out;
} }
if (detach_subchain) {
/* to_cow_parent is already drained because from is drained */
bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran);
}
refresh_list = g_slist_prepend(refresh_list, to); refresh_list = g_slist_prepend(refresh_list, to);
refresh_list = g_slist_prepend(refresh_list, from); refresh_list = g_slist_prepend(refresh_list, from);
@ -5450,7 +5443,7 @@ out:
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp) Error **errp)
{ {
return bdrv_replace_node_common(from, to, true, false, errp); return bdrv_replace_node_common(from, to, true, errp);
} }
int bdrv_drop_filter(BlockDriverState *bs, Error **errp) int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
@ -5466,7 +5459,7 @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
bdrv_drained_begin(child_bs); bdrv_drained_begin(child_bs);
bdrv_graph_wrlock(); bdrv_graph_wrlock();
ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); ret = bdrv_replace_node_common(bs, child_bs, true, errp);
bdrv_graph_wrunlock(); bdrv_graph_wrunlock();
bdrv_drained_end(child_bs); bdrv_drained_end(child_bs);
@ -5917,17 +5910,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
updated_children = g_slist_prepend(updated_children, c); updated_children = g_slist_prepend(updated_children, c);
} }
/* bdrv_replace_node_common(top, base, false, &local_err);
* It seems correct to pass detach_subchain=true here, but it triggers
* one more yet not fixed bug, when due to nested aio_poll loop we switch to
* another drained section, which modify the graph (for example, removing
* the child, which we keep in updated_children list). So, it's a TODO.
*
* Note, bug triggered if pass detach_subchain=true here and run
* test-bdrv-drain. test_drop_intermediate_poll() test-case will crash.
* That's a FIXME.
*/
bdrv_replace_node_common(top, base, false, false, &local_err);
bdrv_graph_wrunlock(); bdrv_graph_wrunlock();
if (local_err) { if (local_err) {

View file

@ -28,6 +28,7 @@
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "system/qtest.h" #include "system/qtest.h"
#include "qapi/error.h"
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
@ -56,13 +57,25 @@ static bool bool_from_onoffauto(OnOffAuto val, bool def)
} }
} }
void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, bool block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
enum OnOffAuto account_failed) enum OnOffAuto account_failed, uint32_t *stats_intervals,
uint32_t num_stats_intervals, Error **errp)
{ {
stats->account_invalid = bool_from_onoffauto(account_invalid, stats->account_invalid = bool_from_onoffauto(account_invalid,
stats->account_invalid); stats->account_invalid);
stats->account_failed = bool_from_onoffauto(account_failed, stats->account_failed = bool_from_onoffauto(account_failed,
stats->account_failed); stats->account_failed);
if (stats_intervals) {
for (int i = 0; i < num_stats_intervals; i++) {
if (stats_intervals[i] <= 0) {
error_setg(errp, "Invalid interval length: %u", stats_intervals[i]);
return false;
}
block_acct_add_interval(stats, stats_intervals[i]);
}
g_free(stats_intervals);
}
return true;
} }
void block_acct_cleanup(BlockAcctStats *stats) void block_acct_cleanup(BlockAcctStats *stats)

View file

@ -63,9 +63,10 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs,
Coroutine *co) Coroutine *co)
{ {
Request *req = g_new(Request, 1); Request *req = g_new(Request, 1);
AioContext *ctx = qemu_coroutine_get_aio_context(co);
*req = (Request) { *req = (Request) {
.co = co, .co = co,
.bh = aio_bh_new(bdrv_get_aio_context(bs), blkreplay_bh_cb, req), .bh = aio_bh_new(ctx, blkreplay_bh_cb, req),
}; };
replay_block_event(req->bh, reqid); replay_block_event(req->bh, reqid);
} }

View file

@ -1318,9 +1318,9 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
* section. * section.
*/ */
qemu_mutex_lock(&blk->queued_requests_lock); qemu_mutex_lock(&blk->queued_requests_lock);
/* blk_root_drained_end() has the corresponding blk_inc_in_flight() */
blk_dec_in_flight(blk); blk_dec_in_flight(blk);
qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock); qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock);
blk_inc_in_flight(blk);
qemu_mutex_unlock(&blk->queued_requests_lock); qemu_mutex_unlock(&blk->queued_requests_lock);
} }
} }
@ -2305,6 +2305,17 @@ uint32_t blk_get_request_alignment(BlockBackend *blk)
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
} }
/* Returns the optimal write zeroes alignment, in bytes; guaranteed nonzero */
uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk)
{
BlockDriverState *bs = blk_bs(blk);
IO_CODE();
if (!bs) {
return BDRV_SECTOR_SIZE;
}
return bs->bl.pwrite_zeroes_alignment ?: bs->bl.request_alignment;
}
/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */ /* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
uint64_t blk_get_max_hw_transfer(BlockBackend *blk) uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
{ {
@ -2767,9 +2778,11 @@ static void blk_root_drained_end(BdrvChild *child)
blk->dev_ops->drained_end(blk->dev_opaque); blk->dev_ops->drained_end(blk->dev_opaque);
} }
qemu_mutex_lock(&blk->queued_requests_lock); qemu_mutex_lock(&blk->queued_requests_lock);
while (qemu_co_enter_next(&blk->queued_requests, while (!qemu_co_queue_empty(&blk->queued_requests)) {
&blk->queued_requests_lock)) {
/* Resume all queued requests */ /* Resume all queued requests */
blk_inc_in_flight(blk);
qemu_co_enter_next(&blk->queued_requests,
&blk->queued_requests_lock);
} }
qemu_mutex_unlock(&blk->queued_requests_lock); qemu_mutex_unlock(&blk->queued_requests_lock);
} }

View file

@ -67,11 +67,18 @@ static int block_crypto_read_func(QCryptoBlock *block,
BlockCrypto *crypto = bs->opaque; BlockCrypto *crypto = bs->opaque;
ssize_t ret; ssize_t ret;
if (qemu_in_coroutine()) {
GRAPH_RDLOCK_GUARD();
ret = bdrv_co_pread(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
} else {
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP(); GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_pread(crypto->header ? crypto->header : bs->file, ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0); offset, buflen, buf, 0);
}
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read encryption header"); error_setg_errno(errp, -ret, "Could not read encryption header");
return ret; return ret;
@ -90,11 +97,18 @@ static int block_crypto_write_func(QCryptoBlock *block,
BlockCrypto *crypto = bs->opaque; BlockCrypto *crypto = bs->opaque;
ssize_t ret; ssize_t ret;
if (qemu_in_coroutine()) {
GRAPH_RDLOCK_GUARD();
ret = bdrv_co_pwrite(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
} else {
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP(); GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file, ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0); offset, buflen, buf, 0);
}
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write encryption header"); error_setg_errno(errp, -ret, "Could not write encryption header");
return ret; return ret;
@ -792,7 +806,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
char *buf = NULL; char *buf = NULL;
int64_t size; int64_t size;
bool detached_hdr = bool detached_hdr =
qemu_opt_get_bool(opts, "detached-header", false); qemu_opt_get_bool_del(opts, "detached-header", false);
unsigned int cflags = 0; unsigned int cflags = 0;
int ret; int ret;
Error *local_err = NULL; Error *local_err = NULL;
@ -821,7 +835,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
} }
/* Create protocol layer */ /* Create protocol layer */
ret = bdrv_co_create_file(filename, opts, errp); ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }

View file

@ -162,13 +162,9 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
void *userp, void *sp) void *userp, void *sp)
{ {
BDRVCURLState *s; BDRVCURLState *s = userp;
CURLState *state = NULL;
CURLSocket *socket; CURLSocket *socket;
curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
s = state->s;
socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd)); socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
if (!socket) { if (!socket) {
socket = g_new0(CURLSocket, 1); socket = g_new0(CURLSocket, 1);
@ -262,8 +258,8 @@ read_end:
} }
/* Called with s->mutex held. */ /* Called with s->mutex held. */
static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, static bool coroutine_fn
CURLAIOCB *acb) curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, CURLAIOCB *acb)
{ {
int i; int i;
uint64_t end = start + len; uint64_t end = start + len;
@ -311,6 +307,10 @@ static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
for (j=0; j<CURL_NUM_ACB; j++) { for (j=0; j<CURL_NUM_ACB; j++) {
if (!state->acb[j]) { if (!state->acb[j]) {
state->acb[j] = acb; state->acb[j] = acb;
/* Await ongoing request */
qemu_mutex_unlock(&s->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&s->mutex);
return true; return true;
} }
} }
@ -382,6 +382,16 @@ static void curl_multi_check_completion(BDRVCURLState *s)
acb->ret = error ? -EIO : 0; acb->ret = error ? -EIO : 0;
state->acb[i] = NULL; state->acb[i] = NULL;
qemu_mutex_unlock(&s->mutex); qemu_mutex_unlock(&s->mutex);
/*
* Current AioContext is the BDS context, which may or may not
* be the request (coroutine) context.
* - If it is, the coroutine must have yielded or the FD handler
* (curl_multi_do()/curl_multi_timeout_do()) could not have
* been called and we would not be here
* - If it is not, it doesn't matter whether it has already
* yielded or not; it will be scheduled once it does yield
* So aio_co_wake() is safe to call.
*/
aio_co_wake(acb->co); aio_co_wake(acb->co);
qemu_mutex_lock(&s->mutex); qemu_mutex_lock(&s->mutex);
} }
@ -475,11 +485,11 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
(void *)curl_read_cb) || (void *)curl_read_cb) ||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) || curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) || curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) || curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) || curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) || curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) || curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) { curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1L)) {
goto err; goto err;
} }
if (s->username) { if (s->username) {
@ -520,7 +530,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) { CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
goto err; goto err;
} }
#elif LIBCURL_VERSION_NUM >= 0x071304 #else
if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) || if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) { curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
goto err; goto err;
@ -528,7 +538,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
#endif #endif
#ifdef DEBUG_VERBOSE #ifdef DEBUG_VERBOSE
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) { if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1L)) {
goto err; goto err;
} }
#endif #endif
@ -605,6 +615,7 @@ static void curl_attach_aio_context(BlockDriverState *bs,
assert(!s->multi); assert(!s->multi);
s->multi = curl_multi_init(); s->multi = curl_multi_init();
s->aio_context = new_context; s->aio_context = new_context;
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
@ -803,7 +814,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
} }
s->accept_range = false; s->accept_range = false;
if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) || if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) || curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) { curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE, pstrcpy(state->errmsg, CURL_ERROR_SIZE,
@ -824,22 +835,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
goto out; goto out;
} }
#endif #endif
/* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
* know or the size is zero. From 7.19.4 CURL returns -1 if size is not
* known and zero if it is really zero-length file. */
#if LIBCURL_VERSION_NUM >= 0x071304
if (cl < 0) { if (cl < 0) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE, pstrcpy(state->errmsg, CURL_ERROR_SIZE,
"Server didn't report file size."); "Server didn't report file size.");
goto out; goto out;
} }
#else
if (cl <= 0) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
"Unknown file size or zero-length file.");
goto out;
}
#endif
s->len = cl; s->len = cl;
@ -882,7 +882,7 @@ out_noclean:
return -EINVAL; return -EINVAL;
} }
static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) static void coroutine_fn curl_do_preadv(BlockDriverState *bs, CURLAIOCB *acb)
{ {
CURLState *state; CURLState *state;
int running; int running;
@ -894,10 +894,13 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
qemu_mutex_lock(&s->mutex); qemu_mutex_lock(&s->mutex);
// In case we have the requested data already (e.g. read-ahead), /*
// we can just call the callback and be done. * In case we have the requested data already (e.g. read-ahead),
* we can just call the callback and be done. This may have to
* await an ongoing request, in which case it itself will yield.
*/
if (curl_find_buf(s, start, acb->bytes, acb)) { if (curl_find_buf(s, start, acb->bytes, acb)) {
goto out; goto dont_yield;
} }
// No cache found, so let's start a new request // No cache found, so let's start a new request
@ -912,7 +915,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
if (curl_init_state(s, state) < 0) { if (curl_init_state(s, state) < 0) {
curl_clean_state(state); curl_clean_state(state);
acb->ret = -EIO; acb->ret = -EIO;
goto out; goto dont_yield;
} }
acb->start = 0; acb->start = 0;
@ -927,7 +930,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
if (state->buf_len && state->orig_buf == NULL) { if (state->buf_len && state->orig_buf == NULL) {
curl_clean_state(state); curl_clean_state(state);
acb->ret = -ENOMEM; acb->ret = -ENOMEM;
goto out; goto dont_yield;
} }
state->acb[0] = acb; state->acb[0] = acb;
@ -939,13 +942,16 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
acb->ret = -EIO; acb->ret = -EIO;
curl_clean_state(state); curl_clean_state(state);
goto out; goto dont_yield;
} }
/* Tell curl it needs to kick things off */ /* Tell curl it needs to kick things off */
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
qemu_mutex_unlock(&s->mutex);
qemu_coroutine_yield();
return;
out: dont_yield:
qemu_mutex_unlock(&s->mutex); qemu_mutex_unlock(&s->mutex);
} }
@ -961,10 +967,7 @@ static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
.bytes = bytes .bytes = bytes
}; };
curl_setup_preadv(bs, &acb); curl_do_preadv(bs, &acb);
while (acb.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
return acb.ret; return acb.ret;
} }

Some files were not shown because too many files have changed in this diff Show more