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
# 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_size = 8
emacs_mode = perl

View file

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

View file

@ -83,14 +83,18 @@
.native_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:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always
expire_in: 7 days
paths:
- build/meson-logs/testlog.txt
- build/meson-logs
reports:
junit: build/meson-logs/testlog.junit.xml
junit: build/meson-logs/*.junit.xml
.functional_test_job_template:
extends: .common_test_job_template
@ -104,14 +108,16 @@
when: always
expire_in: 7 days
paths:
- build/tests/results/latest/results.xml
- build/tests/results/latest/test-results
- build/meson-logs
- build/tests/functional/*/*/*.log
reports:
junit: build/tests/results/latest/results.xml
junit: build/meson-logs/*.junit.xml
before_script:
- export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1
- 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:
- cd build
- du -chs ${CI_PROJECT_DIR}/*-cache

View file

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

View file

@ -37,12 +37,12 @@ x64-freebsd-14-build:
NAME: freebsd-14
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
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_RAM: 8G
UPDATE_COMMAND: pkg update; pkg upgrade -y
INSTALL_COMMAND: pkg install -y
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu
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
aarch64-macos-build:

View file

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

View file

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

View file

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

View file

@ -19,3 +19,7 @@
- docker push "$TAG"
after_script:
- 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:
NAME: fedora-rust-nightly
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
expire_in: 7 days
paths:
- build/meson-logs/testlog.txt
- build/meson-logs
reports:
junit: build/meson-logs/testlog.junit.xml
junit: build/meson-logs/*.junit.xml

View file

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

View file

@ -26,9 +26,9 @@
- build/build.ninja
- build/meson-logs
reports:
junit: build/meson-logs/testlog.junit.xml
junit: build/meson-logs/*.junit.xml
include:
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.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:
GIT_DEPTH: 1
needs:
job: python-container
- job: python-container
check-python-tox:
extends: .base_job_template
@ -45,7 +45,7 @@ check-python-tox:
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
QEMU_JOB_OPTIONAL: 1
needs:
job: python-container
- job: python-container
check-rust-tools-nightly:
extends: .base_job_template
@ -76,7 +76,7 @@ check-build-units:
stage: build
image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG
needs:
job: amd64-debian-container
- job: amd64-debian-container
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Install Tools"

View file

@ -24,9 +24,10 @@ msys2-64bit:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
expire_in: 7 days
paths:
- build/meson-logs/testlog.txt
- build/meson-logs
- build/cache-log.txt
reports:
junit: "build/meson-logs/testlog.junit.xml"
junit: build/meson-logs/*.junit.xml
before_script:
- Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)"
- If ( !(Test-Path -Path msys64\var\cache ) ) {
@ -77,7 +78,7 @@ msys2-64bit:
git grep make sed
mingw-w64-x86_64-binutils
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-glib2
mingw-w64-x86_64-libnfs
@ -87,13 +88,14 @@ msys2-64bit:
mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-python
mingw-w64-x86_64-zstd"
- .\msys64\usr\bin\bash -lc "pacman -Sc --noconfirm"
- Write-Output "Running build at $(Get-Date -Format u)"
- $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc)
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR"
- $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:CC = "ccache gcc"
- mkdir build
@ -102,5 +104,7 @@ msys2-64bit:
- ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS"
- ..\msys64\usr\bin\bash -lc "make -j$env:JOBS"
- ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;"
- ..\msys64\usr\bin\bash -lc "ls -lR /var/cache > cache-log.txt"
- ..\msys64\usr\bin\bash -lc "du -sh ."
- ..\msys64\usr\bin\bash -lc "ccache --show-stats"
- 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
[submodule "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
[submodule "roms/skiboot"]
path = roms/skiboot
@ -27,6 +28,7 @@
url = https://gitlab.com/qemu-project/seabios-hppa.git
[submodule "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
[submodule "roms/edk2"]
path = roms/edk2

View file

@ -4,48 +4,48 @@
# See https://github.com/stefanha/git-publish for more information
#
[gitpublishprofile "default"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "rfc"]
base = master
base = origin/master
prefix = RFC PATCH
to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "stable"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cc = qemu-stable@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "trivial"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cc = qemu-trivial@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "block"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cc = qemu-block@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "arm"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cc = qemu-arm@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "s390"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cc = qemu-s390@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "ppc"]
base = master
base = origin/master
to = qemu-devel@nongnu.org
cc = qemu-ppc@nongnu.org
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 Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.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>
Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca>
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>
Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.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>
Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.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 Wei-Ren <chenwj@iis.sinica.edu.tw>
Christophe Lyon <christophe.lyon@st.com>
Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
Collin L. Walling <walling@linux.ibm.com>
Daniel P. Berrangé <berrange@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
ninja-cmd-goals =
ifneq ($(NINJA),)
Makefile.ninja: build.ninja
$(quiet-@){ \
@ -150,7 +152,7 @@ NINJAFLAGS = \
$(or $(filter -l% -j%, $(MAKEFLAGS)), \
$(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \
-d keepdepfile
ninja-cmd-goals = $(or $(MAKECMDGOALS), all)
ninja-cmd-goals += $(or $(MAKECMDGOALS), all)
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
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
bool
config MSHV
bool
config XEN
bool
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);
do {
qemu_process_cpu_events(cpu);
bql_unlock();
#ifndef _WIN32
do {
@ -57,7 +58,6 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_sem_wait(&cpu->sem);
#endif
bql_lock();
qemu_wait_io_event(cpu);
} while (!cpu->unplug);
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)
{
if (!cpu->vcpu_dirty) {
hvf_get_registers(cpu);
hvf_arch_get_registers(cpu);
cpu->vcpu_dirty = true;
}
}
@ -148,10 +148,11 @@ static int hvf_init_vcpu(CPUState *cpu)
sigact.sa_handler = dummy_signal;
sigaction(SIG_IPI, &sigact, NULL);
#ifdef __aarch64__
pthread_sigmask(SIG_BLOCK, NULL, &cpu->accel->unblock_ipi_mask);
sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI);
cpu->accel->guest_debug_enabled = false;
#ifdef __aarch64__
r = hv_vcpu_create(&cpu->accel->fd,
(hv_vcpu_exit_t **)&cpu->accel->exit, NULL);
#else
@ -160,8 +161,6 @@ static int hvf_init_vcpu(CPUState *cpu)
assert_hvf_ok(r);
cpu->vcpu_dirty = true;
cpu->accel->guest_debug_enabled = false;
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);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
r = hvf_vcpu_exec(cpu);
r = hvf_arch_vcpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(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);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);

View file

@ -32,11 +32,13 @@
#include "system/runstate.h"
#include "system/cpus.h"
#include "system/accel-blocker.h"
#include "system/physmem.h"
#include "system/ramblock.h"
#include "accel/accel-ops.h"
#include "qemu/bswap.h"
#include "exec/tswap.h"
#include "exec/target_page.h"
#include "system/memory.h"
#include "system/ram_addr.h"
#include "qemu/event_notifier.h"
#include "qemu/main-loop.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)
{
KVMState *s = kvm_state;
struct kvm_userspace_memory_region2 mem;
struct kvm_userspace_memory_region2 mem = {};
int ret;
mem.slot = slot->slot | (kml->as_id << 16);
@ -414,7 +416,7 @@ err:
return ret;
}
void kvm_park_vcpu(CPUState *cpu)
static void kvm_park_vcpu(CPUState *cpu)
{
struct KVMParkedVcpu *vcpu;
@ -426,7 +428,7 @@ void kvm_park_vcpu(CPUState *cpu)
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;
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 (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;
}
@ -756,7 +759,7 @@ static void kvm_slot_sync_dirty_pages(KVMSlot *slot)
ram_addr_t start = slot->ram_start_offset;
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)
@ -1595,7 +1598,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
mem->guest_memfd = mr->ram_block->guest_memfd;
mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host;
mem->guest_memfd_offset = mem->guest_memfd >= 0 ?
(uint8_t*)ram - mr->ram_block->host : 0;
kvm_slot_init_dirty_bitmap(mem);
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_guest_memfd_supported =
kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
kvm_vm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_vm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
@ -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;
int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err);
int ret = kvm_arch_put_registers(cpu, state, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Restoring resisters after reset: ");
error_reportf_err(err, "Restoring resisters %s: ", desc);
} else {
error_report("Failed to put registers after reset: %s",
error_report("Failed to put registers %s: %s", desc,
strerror(-ret));
}
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
return 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)
@ -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)
{
Error *err = NULL;
int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Putting registers after init: ");
} else {
error_report("Failed to put registers after init: %s",
strerror(-ret));
}
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_FULL_STATE, "after init")) {
exit(1);
}
cpu->vcpu_dirty = false;
}
void kvm_cpu_synchronize_post_init(CPUState *cpu)
@ -3029,10 +3033,6 @@ static void kvm_eat_signals(CPUState *cpu)
if (kvm_immediate_exit) {
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;
}
@ -3159,7 +3159,6 @@ int kvm_cpu_exec(CPUState *cpu)
trace_kvm_cpu_exec();
if (kvm_arch_process_async_events(cpu)) {
qatomic_set(&cpu->exit_request, 0);
return EXCP_HLT;
}
@ -3170,24 +3169,16 @@ int kvm_cpu_exec(CPUState *cpu)
MemTxAttrs attrs;
if (cpu->vcpu_dirty) {
Error *err = NULL;
ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Putting registers after init: ");
} else {
error_report("Failed to put registers after init: %s",
strerror(-ret));
}
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RUNTIME_STATE,
"at runtime")) {
ret = -1;
break;
}
cpu->vcpu_dirty = false;
}
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();
/*
* 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();
}
/* 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);
/*
* 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);
#ifdef KVM_HAVE_MCE_INJECTION
@ -3346,7 +3339,6 @@ int kvm_cpu_exec(CPUState *cpu)
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
qatomic_set(&cpu->exit_request, 0);
return ret;
}
@ -3381,10 +3373,10 @@ int kvm_vm_ioctl(KVMState *s, unsigned long type, ...)
trace_kvm_vm_ioctl(type, arg);
accel_ioctl_begin();
ret = ioctl(s->vmfd, type, arg);
accel_ioctl_end();
if (ret == -1) {
ret = -errno;
}
accel_ioctl_end();
return ret;
}
@ -3421,10 +3413,10 @@ int kvm_device_ioctl(int fd, unsigned long type, ...)
trace_kvm_device_ioctl(fd, type, arg);
accel_ioctl_begin();
ret = ioctl(fd, type, arg);
accel_ioctl_end();
if (ret == -1) {
ret = -errno;
}
accel_ioctl_end();
return ret;
}
@ -3731,7 +3723,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
have_sigbus_pending = true;
pending_sigbus_addr = addr;
pending_sigbus_code = code;
qatomic_set(&cpu->exit_request, 1);
qatomic_set(&cpu->exit_request, true);
return 0;
#else
return 1;

View file

@ -1,6 +1,6 @@
common_ss.add(files('accel-common.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'))
subdir('tcg')
@ -10,6 +10,7 @@ if have_system
subdir('kvm')
subdir('xen')
subdir('stubs')
subdir('mshv')
endif
# 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_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_MSHV', if_false: files('mshv-stub.c'))
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_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)
#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 GEN_ATOMIC_HELPERS

View file

@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return ret;
}
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
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_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, val);
#else
ret = qatomic_xchg__nocheck(haddr, val);
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
@ -119,6 +122,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
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) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
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)
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE < 16 */
#endif /* DATA SIZE == 16 */
#undef END
@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return BSWAP(ret);
}
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
@ -233,7 +268,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
DATA_SIZE, retaddr);
ABI_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, BSWAP(val));
#else
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
@ -244,6 +283,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
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) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
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 GEN_ATOMIC_HELPER_FN
#endif /* DATA_SIZE < 16 */
#endif /* DATA_SIZE == 16 */
#undef END
#endif /* DATA_SIZE > 1 */

View file

@ -40,6 +40,7 @@
#include "exec/replay-core.h"
#include "system/tcg.h"
#include "exec/helper-proto-common.h"
#include "tcg-accel-ops.h"
#include "tb-jmp-cache.h"
#include "tb-hash.h"
#include "tb-context.h"
@ -748,6 +749,20 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
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)
{
if (!icount_enabled()) {
@ -774,44 +789,47 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request.
* 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);
if (unlikely(qatomic_read(&cpu->interrupt_request))) {
int interrupt_request;
#ifdef CONFIG_USER_ONLY
assert(!cpu_test_interrupt(cpu, ~0));
#else
if (unlikely(cpu_test_interrupt(cpu, ~0))) {
bql_lock();
interrupt_request = cpu->interrupt_request;
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
/* 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;
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG);
cpu->exception_index = EXCP_DEBUG;
bql_unlock();
return true;
}
#if !defined(CONFIG_USER_ONLY)
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
/* Do nothing */
} else if (interrupt_request & CPU_INTERRUPT_HALT) {
} else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) {
replay_interrupt();
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
cpu->halted = 1;
cpu->exception_index = EXCP_HLT;
bql_unlock();
return true;
} else {
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();
tcg_ops->cpu_exec_reset(cpu);
bql_unlock();
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:
* False when the interrupt isn't processed,
@ -836,13 +854,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
cpu->exception_index = -1;
*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 (interrupt_request & CPU_INTERRUPT_EXITTB) {
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB);
/* ensure that no TB jump will be modified as
the program flow was changed */
*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 */
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)) {
qatomic_set(&cpu->exit_request, 0);
/*
* Finally, check if we need to exit to the main loop.
* 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) {
cpu->exception_index = EXCP_INTERRUPT;
}

View file

@ -25,6 +25,7 @@
#include "accel/tcg/probe.h"
#include "exec/page-protection.h"
#include "system/memory.h"
#include "system/physmem.h"
#include "accel/tcg/cpu-ldst-common.h"
#include "accel/tcg/cpu-mmu-index.h"
#include "exec/cputlb.h"
@ -89,9 +90,6 @@
*/
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)
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,
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;
}
@ -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,
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,
@ -292,7 +290,7 @@ static void tlb_flush_one_mmuidx_locked(CPUState *cpu, int mmu_idx,
int64_t now)
{
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_flush_locked(desc, fast);
@ -331,7 +329,7 @@ void tlb_init(CPUState *cpu)
cpu->neg.tlb.c.dirty = 0;
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);
for (i = 0; i < NB_MMU_MODES; 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(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)
{
uint16_t asked = data.host_int;
uint16_t all_dirty, work, to_clean;
MMUIdxMap asked = data.host_int;
MMUIdxMap all_dirty, work, to_clean;
int64_t now = get_clock_realtime();
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);
@ -422,7 +420,7 @@ void tlb_flush(CPUState *cpu)
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;
@ -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,
vaddr addr,
uint16_t idxmap)
MMUIdxMap idxmap)
{
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 = 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);
}
typedef struct {
vaddr addr;
uint16_t idxmap;
MMUIdxMap idxmap;
} TLBFlushPageByMMUIdxData;
/**
@ -599,7 +597,7 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu,
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);
@ -618,7 +616,7 @@ void tlb_flush_page(CPUState *cpu, vaddr addr)
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr,
uint16_t idxmap)
MMUIdxMap 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)
{
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);
/*
@ -715,8 +713,8 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
typedef struct {
vaddr addr;
vaddr len;
uint16_t idxmap;
uint16_t bits;
MMUIdxMap idxmap;
unsigned bits;
} TLBFlushRangeData;
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,
vaddr len, uint16_t idxmap,
vaddr len, MMUIdxMap idxmap,
unsigned bits)
{
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,
uint16_t idxmap, unsigned bits)
MMUIdxMap idxmap, unsigned 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,
vaddr addr,
vaddr len,
uint16_t idxmap,
MMUIdxMap idxmap,
unsigned bits)
{
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,
vaddr addr,
uint16_t idxmap,
MMUIdxMap idxmap,
unsigned bits)
{
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 */
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,
DIRTY_MEMORY_CODE);
}
@ -867,7 +865,7 @@ void tlb_protect_code(ram_addr_t ram_addr)
tested for self modifying code */
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);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx];
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
unsigned int n = tlb_n_entries(fast);
unsigned int i;
@ -1085,7 +1083,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
if (prot & PAGE_WRITE) {
if (section->readonly) {
write_flags |= TLB_DISCARD_WRITE;
} else if (cpu_physical_memory_is_clean(iotlb)) {
} else if (physical_memory_is_clean(iotlb)) {
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) {
/* 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);
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);
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);
}
@ -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
* 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. */
if (!cpu_physical_memory_is_clean(ram_addr)) {
if (!physical_memory_is_clean(ram_addr)) {
trace_memory_notdirty_set_dirty(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)) {
/* Alignment has not been checked by tlb_fill_align. */
int a_bits = memop_alignment_bits(memop);
/*
* This alignment check differs from the one above, in that this is
* based on the atomicity of the operation. The intended use case is
* the ARM memory type field of each PTE, where access to pages with
* Device memory type require alignment.
*/
if (unlikely(flags & TLB_CHECK_ALIGNED)) {
int at_bits = memop_atomicity_bits(memop);
a_bits = MAX(a_bits, at_bits);
}
int a_bits = memop_tlb_alignment_bits(memop, flags & TLB_CHECK_ALIGNED);
if (unlikely(addr & ((1 << a_bits) - 1))) {
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)
{
bool crosspage;
vaddr last;
int flags;
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].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;
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);
last = addr + l->page[0].size - 1;
crosspage = (addr ^ last) & TARGET_PAGE_MASK;
if (likely(!crosspage)) {
flags = l->page[0].flags;
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
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 {
/* 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[0].size = size0;
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
* second lookup potentially resized, refresh first CPUTLBEntryFull.
* Lookup and recognize exceptions from the second page.
* 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)) {
uintptr_t index = tlb_index(cpu, l->mmu_idx, addr);
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,
* including scoreboard index, will be optimized out.
* User-mode calls tb_flush when setting this flag. In system-mode, all
* vcpus are created before generating code.
* User-mode flushes all TBs when setting this flag.
* In system-mode, all vcpus are created before generating code.
*/
if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) {
return tcg_constant_i32(current_cpu->cpu_index);

View file

@ -36,8 +36,11 @@
#include "internal-common.h"
#ifdef CONFIG_USER_ONLY
#include "user/page-protection.h"
#define runstate_is_running() true
#else
#include "system/runstate.h"
#endif
#include "trace.h"
/* List iterators for lists of tagged pointers in TranslationBlock. */
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \
@ -88,7 +91,10 @@ static IntervalTreeRoot tb_root;
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));
}
@ -756,17 +762,20 @@ static void tb_remove(TranslationBlock *tb)
}
#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();
/* If it is already been done on request of another CPU, just retry. */
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
goto done;
}
did_flush = true;
trace_tb_flush();
assert(tcg_enabled());
/* Note that cpu_in_serial_context checks cpu_in_exclusive_context. */
assert(!runstate_is_running() ||
(current_cpu && cpu_in_serial_context(current_cpu)));
CPU_FOREACH(cpu) {
tcg_flush_jmp_cache(cpu);
@ -778,26 +787,24 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
tcg_region_reset_all();
/* XXX: flush processor icache at this point if cache flush is expensive */
qatomic_inc(&tb_ctx.tb_flush_count);
done:
mmap_unlock();
if (did_flush) {
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()) {
unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count);
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,
async_safe_run_on_cpu(cs, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
}
}
}
/* remove @orig from its @n_orig-th jump list */
@ -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 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;
TB_FOR_EACH_JMP(dest, tb, n) {
if (tb == orig && n == n_orig) {
@ -1154,7 +1169,6 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
page_collection_unlock(pages);
/* Force execution of one insn next time. */
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
mmap_unlock();
cpu_loop_exit_noexc(cpu);
}
}

View file

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

View file

@ -10,9 +10,6 @@
#ifndef 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 */
void mttcg_start_vcpu_thread(CPUState *cpu);

View file

@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused)
CPUState *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();
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);
/* 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);
/* process any pending work */
CPU_FOREACH(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;
/* process any pending work */
cpu->exit_request = 1;
while (1) {
/* Only used for icount_enabled() */
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();
replay_mutex_lock();
bql_lock();
@ -242,10 +259,17 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu;
}
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
/* Store rr_current_cpu before evaluating cpu_can_run(). */
while (cpu && cpu_work_list_empty(cpu)) {
/*
* Store rr_current_cpu before evaluating cpu->exit_request.
* Pairs with rr_kick_next_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;
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. */
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();

View file

@ -82,8 +82,6 @@ int tcg_cpu_exec(CPUState *cpu)
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
qatomic_set_mb(&cpu->exit_request, 0);
return ret;
}
@ -97,7 +95,7 @@ static void tcg_cpu_reset_hold(CPUState *cpu)
/* mask must never be zero, except for A20 change call */
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
@ -206,7 +204,7 @@ static void tcg_accel_ops_init(AccelClass *ac)
if (qemu_tcg_mttcg_enabled()) {
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;
} else {
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);
void tcg_handle_interrupt(CPUState *cpu, int mask);
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
void tcg_kick_vcpu_thread(CPUState *cpu);
#endif /* TCG_ACCEL_OPS_H */

View file

@ -38,6 +38,8 @@
#include "qemu/target-info.h"
#ifndef CONFIG_USER_ONLY
#include "hw/boards.h"
#include "exec/tb-flush.h"
#include "system/runstate.h"
#endif
#include "accel/accel-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;
#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)
{
TCGState *s = TCG_STATE(as);
@ -124,6 +143,8 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
default:
g_assert_not_reached();
}
qemu_add_vm_change_state_handler(tcg_vm_change_state, NULL);
#endif
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)
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
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
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_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
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_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""
# 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);
if (unlikely(!tb)) {
/* 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();
/* Make the execution loop process the flush as soon as possible. */
cpu->exception_index = EXCP_INTERRUPT;
@ -321,6 +326,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
if (unlikely(gen_code_size < 0)) {
switch (gen_code_size) {
case -1:
trace_tb_gen_code_buffer_overflow("setjmp_gen_code");
/*
* 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);
if (unlikely(search_size < 0)) {
trace_tb_gen_code_buffer_overflow("encode_search");
tb_unlock_pages(tb);
goto buffer_overflow;
}

View file

@ -38,6 +38,7 @@
#include "qemu/int128.h"
#include "trace.h"
#include "tcg/tcg-ldst.h"
#include "tcg-accel-ops.h"
#include "backend-ldst.h"
#include "internal-common.h"
#include "tb-internal.h"
@ -46,11 +47,15 @@ __thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL
void cpu_interrupt(CPUState *cpu, int mask)
void qemu_cpu_kick(CPUState *cpu)
{
g_assert(bql_locked());
cpu->interrupt_request |= mask;
qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
tcg_kick_vcpu_thread(cpu);
}
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);
}
/* 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],
* 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]. */
static bool pageflags_set_clear(vaddr start, vaddr last,
int set_flags, int clear_flags)
@ -372,7 +326,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
restart:
p = pageflags_find(start, last);
if (!p) {
if (set_flags) {
if (set_flags & PAGE_VALID) {
pageflags_create_merge(start, last, set_flags);
}
goto done;
@ -386,11 +340,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
/*
* 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)
&& (!(merge_flags & PAGE_EXEC)
|| (merge_flags & ~p_flags & PAGE_WRITE))) {
|| (merge_flags & ~p_flags & PAGE_WRITE)
|| (clear_flags & PAGE_VALID))) {
inval_tb = true;
}
@ -399,7 +354,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
* attempting to merge with adjacent regions.
*/
if (start == p_start && last == p_last) {
if (merge_flags) {
if (merge_flags & PAGE_VALID) {
p->flags = merge_flags;
} else {
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);
if (last < p_last) {
if (merge_flags) {
if (merge_flags & PAGE_VALID) {
pageflags_create(start, last, merge_flags);
}
pageflags_create(last + 1, p_last, p_flags);
} else {
if (merge_flags) {
if (merge_flags & PAGE_VALID) {
pageflags_create(start, p_last, merge_flags);
}
if (p_last < last) {
@ -433,18 +388,18 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
}
}
} else {
if (start < p_start && set_flags) {
if (start < p_start && (set_flags & PAGE_VALID)) {
pageflags_create(start, p_start - 1, set_flags);
}
if (last < p_last) {
interval_tree_remove(&p->itree, &pageflags_root);
p->itree.start = last + 1;
interval_tree_insert(&p->itree, &pageflags_root);
if (merge_flags) {
if (merge_flags & PAGE_VALID) {
pageflags_create(start, last, merge_flags);
}
} else {
if (merge_flags) {
if (merge_flags & PAGE_VALID) {
p->flags = merge_flags;
} else {
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);
goto restart;
}
if (set_flags) {
if (set_flags & PAGE_VALID) {
pageflags_create(start, last, set_flags);
}
@ -500,42 +455,36 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
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
a missing call to h2g_valid. */
/*
* This function should never be called with addresses outside the
* guest address space. If this assert fires, it probably indicates
* a missing call to h2g_valid.
*/
assert(start <= last);
assert(last <= guest_addr_max);
/* Only set PAGE_ANON with new mappings. */
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
assert_memory_lock();
start &= TARGET_PAGE_MASK;
last |= ~TARGET_PAGE_MASK;
if (!(flags & PAGE_VALID)) {
flags = 0;
} else {
reset = flags & PAGE_RESET;
flags &= ~PAGE_RESET;
if (flags & PAGE_WRITE) {
flags |= PAGE_WRITE_ORG;
if (set_flags & PAGE_WRITE) {
set_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);
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,
~(reset ? 0 : PAGE_STICKY));
}
if (inval_tb) {
if (pageflags_set_clear(start, last, set_flags, clear_flags)) {
tb_invalidate_phys_range(NULL, start, last);
}
}

View file

@ -26,7 +26,7 @@
#include <alsa/asoundlib.h>
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "audio.h"
#include "qemu/audio.h"
#include "trace.h"
#pragma GCC diagnostic ignored "-Waddress"
@ -41,7 +41,7 @@ struct pollhlp {
struct pollfd *pfds;
int count;
int mask;
AudioState *s;
AudioBackend *s;
};
typedef struct ALSAVoiceOut {
@ -264,7 +264,7 @@ static int alsa_poll_in (HWVoiceIn *hw)
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) {
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;
case AUDIO_FORMAT_S16:
if (endianness) {
return SND_PCM_FORMAT_S16_BE;
} else {
return SND_PCM_FORMAT_S16_LE;
}
return big_endian ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE;
case AUDIO_FORMAT_U16:
if (endianness) {
return SND_PCM_FORMAT_U16_BE;
} else {
return SND_PCM_FORMAT_U16_LE;
}
return big_endian ? SND_PCM_FORMAT_U16_BE : SND_PCM_FORMAT_U16_LE;
case AUDIO_FORMAT_S32:
if (endianness) {
return SND_PCM_FORMAT_S32_BE;
} else {
return SND_PCM_FORMAT_S32_LE;
}
return big_endian ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE;
case AUDIO_FORMAT_U32:
if (endianness) {
return SND_PCM_FORMAT_U32_BE;
} else {
return SND_PCM_FORMAT_U32_LE;
}
return big_endian ? SND_PCM_FORMAT_U32_BE : SND_PCM_FORMAT_U32_LE;
case AUDIO_FORMAT_F32:
if (endianness) {
return SND_PCM_FORMAT_FLOAT_BE;
} else {
return SND_PCM_FORMAT_FLOAT_LE;
}
return big_endian ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE;
default:
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 = {
.name = "alsa",
.descr = "ALSA http://www.alsa-project.org",
.init = alsa_audio_init,
.fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops,

View file

@ -23,11 +23,12 @@
*/
#include "qemu/osdep.h"
#include "audio/audio.h"
#include "audio_int.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qobject/qdict.h"
#include "qemu/error-report.h"
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
@ -36,6 +37,8 @@ void hmp_info_capture(Monitor *mon, const QDict *qdict)
int i;
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) {
monitor_printf(mon, "[%d]: ", i);
s->ops.info (s->opaque);
@ -48,6 +51,8 @@ void hmp_stopcapture(Monitor *mon, const QDict *qdict)
int n = qdict_get_int(qdict, "n");
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) {
if (i == n) {
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");
CaptureState *s;
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) {
error_report_err(local_err);

View file

@ -23,9 +23,8 @@
*/
#include "qemu/osdep.h"
#include "audio.h"
#include "qemu/audio.h"
#include "migration/vmstate.h"
#include "monitor/monitor.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "qapi/clone-visitor.h"
@ -33,7 +32,6 @@
#include "qapi/qapi-visit-audio.h"
#include "qapi/qapi-commands-audio.h"
#include "qobject/qdict.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/module.h"
@ -41,7 +39,6 @@
#include "system/system.h"
#include "system/replay.h"
#include "system/runstate.h"
#include "ui/qemu-spice.h"
#include "trace.h"
#define AUDIO_CAP "audio"
@ -102,9 +99,7 @@ static audio_driver *audio_driver_lookup(const char *name)
return NULL;
}
static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
QTAILQ_HEAD_INITIALIZER(audio_states);
static AudioState *default_audio_state;
static AudioBackend *default_audio_be;
const struct mixeng_volume nominal_volume = {
.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_float == is_float
&& 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)
@ -325,7 +320,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
info->nchannels = as->nchannels;
info->bytes_per_frame = as->nchannels * mul;
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)
@ -385,7 +380,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
/*
* Capture
*/
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioBackend *s,
struct audsettings *as)
{
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) {
audcnotification_e cmd;
@ -424,11 +419,11 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
{
HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw;
int enabled = 0;
bool enabled = false;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) {
enabled = 1;
enabled = true;
break;
}
}
@ -465,7 +460,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
static int audio_attach_capture (HWVoiceOut *hw)
{
AudioState *s = hw->s;
AudioBackend *s = hw->s;
CaptureVoiceOut *cap;
audio_detach_capture (hw);
@ -480,7 +475,7 @@ static int audio_attach_capture (HWVoiceOut *hw)
sw = &sc->sw;
sw->hw = hw_cap;
sw->info = hw->info;
sw->empty = 1;
sw->empty = true;
sw->active = hw->enabled;
sw->vol = nominal_volume;
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
*/
static int audio_is_timer_needed(AudioState *s)
static int audio_is_timer_needed(AudioBackend *s)
{
HWVoiceIn *hwi = NULL;
HWVoiceOut *hwo = NULL;
@ -821,7 +816,7 @@ static int audio_is_timer_needed(AudioState *s)
return 0;
}
static void audio_reset_timer (AudioState *s)
static void audio_reset_timer(AudioBackend *s)
{
if (audio_is_timer_needed(s)) {
timer_mod_anticipate_ns(s->ts,
@ -843,7 +838,7 @@ static void audio_reset_timer (AudioState *s)
static void audio_timer (void *opaque)
{
int64_t now, diff;
AudioState *s = opaque;
AudioBackend *s = opaque;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
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;
}
void AUD_set_active_out (SWVoiceOut *sw, int on)
void AUD_set_active_out(SWVoiceOut *sw, bool on)
{
HWVoiceOut *hw;
@ -926,14 +921,14 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw = sw->hw;
if (sw->active != on) {
AudioState *s = sw->s;
AudioBackend *s = sw->s;
SWVoiceOut *temp_sw;
SWVoiceCap *sc;
if (on) {
hw->pending_disable = 0;
if (!hw->enabled) {
hw->enabled = 1;
hw->enabled = true;
if (s->vm_running) {
if (hw->pcm_ops->enable_out) {
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;
@ -974,12 +969,12 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
hw = sw->hw;
if (sw->active != on) {
AudioState *s = sw->s;
AudioBackend *s = sw->s;
SWVoiceIn *temp_sw;
if (on) {
if (!hw->enabled) {
hw->enabled = 1;
hw->enabled = true;
if (s->vm_running) {
if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, true);
@ -998,7 +993,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
}
if (nb_active == 1) {
hw->enabled = 0;
hw->enabled = false;
if (hw->pcm_ops->enable_in) {
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;
}
static void audio_run_out (AudioState *s)
static void audio_run_out(AudioBackend *s)
{
HWVoiceOut *hw = NULL;
SWVoiceOut *sw;
@ -1157,8 +1152,8 @@ static void audio_run_out (AudioState *s)
sw = hw->sw_head.lh_first;
if (hw->pending_disable) {
hw->enabled = 0;
hw->pending_disable = 0;
hw->enabled = false;
hw->pending_disable = false;
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false);
}
@ -1211,13 +1206,13 @@ static void audio_run_out (AudioState *s)
#ifdef DEBUG_OUT
dolog ("Disabling voice\n");
#endif
hw->enabled = 0;
hw->pending_disable = 0;
hw->enabled = false;
hw->pending_disable = false;
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false);
}
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);
}
continue;
@ -1262,7 +1257,7 @@ static void audio_run_out (AudioState *s)
sw->total_hw_samples_mixed -= played;
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;
}
static void audio_run_in (AudioState *s)
static void audio_run_in(AudioBackend *s)
{
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;
@ -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_in(s);
@ -1564,14 +1559,14 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
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)
{
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) {
drv->pcm_ops->get_buffer_in = audio_generic_get_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_in(s, drv, 0);
s->drv = drv;
return 0;
if (dev->timer_period <= 0) {
s->period_ticks = 1;
} else {
if (local_err) {
error_propagate(errp, local_err);
} else {
error_setg(errp, "Could not init `%s' audio driver", drv->name);
}
return -1;
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
}
return true;
}
static void audio_vm_change_state_handler (void *opaque, bool running,
RunState state)
{
AudioState *s = opaque;
AudioBackend *s = opaque;
HWVoiceOut *hwo = NULL;
HWVoiceIn *hwi = NULL;
@ -1617,8 +1611,26 @@ static void audio_vm_change_state_handler (void *opaque, bool running,
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;
HWVoiceIn *hwi, *hwin;
@ -1664,17 +1676,24 @@ static void free_audio_state(AudioState *s)
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)
{
default_audio_state = NULL;
while (!QTAILQ_EMPTY(&audio_states)) {
AudioState *s = QTAILQ_FIRST(&audio_states);
QTAILQ_REMOVE(&audio_states, s, list);
free_audio_state(s);
}
default_audio_be = NULL;
object_unparent(get_audiodevs_root());
}
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_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
* 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;
const char *drvname;
VMChangeStateEntry *vmse;
AudioState *s;
AudioBackend *s;
struct audio_driver *driver;
s = g_new0(AudioState, 1);
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);
s = AUDIO_BACKEND(object_new(TYPE_AUDIO_BACKEND));
if (dev) {
/* -audiodev option */
@ -1750,7 +1757,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
drvname = AudiodevDriver_str(dev->driver);
driver = audio_driver_lookup(drvname);
if (driver) {
done = !audio_driver_init(s, driver, dev, errp);
done = audio_driver_init(s, driver, dev, errp);
} else {
error_setg(errp, "Unknown audio driver `%s'", drvname);
}
@ -1758,7 +1765,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
goto out;
}
} else {
assert(!default_audio_state);
assert(!default_audio_be);
for (;;) {
AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
if (!e) {
@ -1770,7 +1777,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
g_free(e);
drvname = AudiodevDriver_str(dev->driver);
driver = audio_driver_lookup(drvname);
if (!audio_driver_init(s, driver, dev, NULL)) {
if (audio_driver_init(s, driver, dev, NULL)) {
break;
}
qapi_free_Audiodev(dev);
@ -1778,33 +1785,22 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
}
}
if (dev->timer_period <= 0) {
s->period_ticks = 1;
} else {
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
if (!object_property_try_add_child(get_audiodevs_root(), dev->id, OBJECT(s), errp)) {
goto out;
}
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);
object_unref(s);
return s;
out:
free_audio_state(s);
object_unref(s);
return NULL;
}
AudioState *audio_get_default_audio_state(Error **errp)
AudioBackend *audio_get_default_audio_be(Error **errp)
{
if (!default_audio_state) {
default_audio_state = audio_init(NULL, errp);
if (!default_audio_state) {
if (!default_audio_be) {
default_audio_be = audio_init(NULL, errp);
if (!default_audio_be) {
if (!QSIMPLEQ_EMPTY(&audiodevs)) {
error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n",
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) {
card->state = audio_get_default_audio_state(errp);
if (!card->state) {
assert(be != NULL);
if (!*be) {
*be = audio_get_default_audio_be(errp);
if (!*be) {
return false;
}
}
card->name = g_strdup (name);
memset (&card->entries, 0, sizeof (card->entries));
QLIST_INSERT_HEAD(&card->state->card_head, card, entries);
return true;
}
void AUD_remove_card (QEMUSoundCard *card)
{
QLIST_REMOVE (card, entries);
g_free (card->name);
}
static struct audio_pcm_ops capture_pcm_ops;
CaptureVoiceOut *AUD_add_capture(
AudioState *s,
AudioBackend *s,
struct audsettings *as,
struct audio_capture_ops *ops,
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)
{
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
audio_set_volume_out(sw, &vol);
}
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
void AUD_set_volume_out(SWVoiceOut *sw, Volume *vol)
{
if (sw) {
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)
{
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
audio_set_volume_in(sw, &vol);
}
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol)
void AUD_set_volume_in(SWVoiceIn *sw, Volume *vol)
{
if (sw) {
HWVoiceIn *hw = sw->hw;
@ -2142,10 +2118,10 @@ void audio_parse_option(const char *opt)
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v);
audio_define(dev);
audio_add_audiodev(dev);
}
void audio_define(Audiodev *dev)
void audio_add_audiodev(Audiodev *dev)
{
AudiodevListEntry *e;
@ -2156,7 +2132,7 @@ void audio_define(Audiodev *dev)
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;
@ -2182,7 +2158,7 @@ audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
.freq = pdo->frequency,
.nchannels = pdo->channels,
.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);
}
AudioState *audio_state_by_name(const char *name, Error **errp)
AudioBackend *audio_be_by_name(const char *name, Error **errp)
{
AudioState *s;
QTAILQ_FOREACH(s, &audio_states, list) {
assert(s->dev);
if (strcmp(name, s->dev->id) == 0) {
return s;
}
}
Object *obj = object_resolve_path_component(get_audiodevs_root(), name);
if (!obj) {
error_setg(errp, "audiodev '%s' not found", name);
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(card->state->dev);
return card->state->dev->id;
assert(be != NULL);
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 {
return "";
}
@ -2320,3 +2312,20 @@ AudiodevList *qmp_query_audiodevs(Error **errp)
}
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 RECIPROCAL */
#endif
#include "qemu/audio.h"
#include "qemu/audio-capture.h"
#include "mixeng.h"
#ifdef CONFIG_GIO
#include <gio/gio.h>
#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_callback {
@ -53,7 +61,7 @@ struct audio_pcm_info {
int swap_endianness;
};
typedef struct AudioState AudioState;
typedef struct AudioBackend AudioBackend;
typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer {
@ -62,10 +70,10 @@ typedef struct STSampleBuffer {
} STSampleBuffer;
typedef struct HWVoiceOut {
AudioState *s;
int enabled;
AudioBackend *s;
bool enabled;
int poll_mode;
int pending_disable;
bool pending_disable;
struct audio_pcm_info info;
f_sample *clip;
@ -83,8 +91,8 @@ typedef struct HWVoiceOut {
} HWVoiceOut;
typedef struct HWVoiceIn {
AudioState *s;
int enabled;
AudioBackend *s;
bool enabled;
int poll_mode;
struct audio_pcm_info info;
@ -104,15 +112,14 @@ typedef struct HWVoiceIn {
} HWVoiceIn;
struct SWVoiceOut {
QEMUSoundCard *card;
AudioState *s;
AudioBackend *s;
struct audio_pcm_info info;
t_sample *conv;
STSampleBuffer resample_buf;
void *rate;
size_t total_hw_samples_mixed;
int active;
int empty;
bool active;
bool empty;
HWVoiceOut *hw;
char *name;
struct mixeng_volume vol;
@ -121,9 +128,8 @@ struct SWVoiceOut {
};
struct SWVoiceIn {
QEMUSoundCard *card;
AudioState *s;
int active;
AudioBackend *s;
bool active;
struct audio_pcm_info info;
void *rate;
size_t total_hw_samples_acquired;
@ -139,11 +145,13 @@ struct SWVoiceIn {
typedef struct audio_driver audio_driver;
struct audio_driver {
const char *name;
const char *descr;
void *(*init) (Audiodev *, Error **);
void (*fini) (void *);
#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
struct audio_pcm_ops *pcm_ops;
int max_voices_out;
@ -187,6 +195,23 @@ struct audio_pcm_ops {
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_get_buffer_in(HWVoiceIn *hw, 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;
};
typedef struct AudioState {
typedef struct AudioBackend {
Object parent;
struct audio_driver *drv;
Audiodev *dev;
void *drv_opaque;
QEMUTimer *ts;
QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
@ -233,9 +259,8 @@ typedef struct AudioState {
bool timer_running;
uint64_t timer_last;
QTAILQ_ENTRY(AudioState) list;
} AudioState;
VMChangeStateEntry *vmse;
} AudioBackend;
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);
void audio_run(AudioState *s, const char *msg);
void audio_run(AudioBackend *s, const char *msg);
const char *audio_application_name(void);

View file

@ -36,7 +36,7 @@
#define HWBUF hw->conv_buf
#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)
{
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);
sw->hw = hw;
sw->active = 0;
sw->active = false;
#ifdef DAC
sw->total_hw_samples_mixed = 0;
sw->empty = 1;
sw->empty = true;
#endif
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)
{
HW *hw = *hwp;
AudioState *s = hw->s;
AudioBackend *s = hw->s;
if (!hw->sw_head.lh_first) {
#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;
}
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))) {
if (hw->enabled) {
@ -251,7 +251,7 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
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)
{
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;
}
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)
{
HW *hw;
@ -398,7 +398,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
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;
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)(
AudioState *s,
AudioBackend *s,
const char *sw_name,
struct audsettings *as
)
@ -473,11 +473,11 @@ static void glue (audio_close_, TYPE) (SW *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 (audio_bug(__func__, !card)) {
dolog ("card=%p\n", card);
if (audio_bug(__func__, !be)) {
dolog("backend=%p\n", be);
return;
}
@ -486,7 +486,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
}
SW *glue (AUD_open_, TYPE) (
QEMUSoundCard *card,
AudioBackend *be,
SW *sw,
const char *name,
void *callback_opaque ,
@ -494,16 +494,15 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as
)
{
AudioState *s;
AudioBackend *s = be;
AudiodevPerDirectionOptions *pdo;
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
card, name, callback_fn, as);
if (audio_bug(__func__, !be || !name || !callback_fn || !as)) {
dolog("backend=%p name=%p callback_fn=%p as=%p\n",
be, name, callback_fn, as);
goto fail;
}
s = card->state;
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
@ -524,7 +523,7 @@ SW *glue (AUD_open_, TYPE) (
}
if (!pdo->fixed_settings && sw) {
glue (AUD_close_, TYPE) (card, sw);
glue(AUD_close_, TYPE)(be, sw);
sw = NULL;
}
@ -548,7 +547,6 @@ SW *glue (AUD_open_, TYPE) (
}
}
sw->card = card;
sw->vol = nominal_volume;
sw->callback.fn = callback_fn;
sw->callback.opaque = callback_opaque;
@ -562,11 +560,11 @@ SW *glue (AUD_open_, TYPE) (
return sw;
fail:
glue (AUD_close_, TYPE) (card, sw);
glue(AUD_close_, TYPE)(be, sw);
return NULL;
}
int glue (AUD_is_active_, TYPE) (SW *sw)
bool glue(AUD_is_active_, TYPE)(SW *sw)
{
return sw ? sw->active : 0;
}

View file

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

View file

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

View file

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

View file

@ -27,12 +27,12 @@
*/
#include "qemu/osdep.h"
#include "audio.h"
#include "qemu/audio.h"
#define AUDIO_CAP "dsound"
#include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include <windows.h>
#include <mmsystem.h>
@ -64,162 +64,154 @@ typedef struct {
dsound *s;
} DSoundVoiceIn;
static void dsound_log_hresult (HRESULT hr)
static const char *dserror(HRESULT hr)
{
const char *str = "BUG";
switch (hr) {
case DS_OK:
str = "The method succeeded";
break;
return "The method succeeded";
#ifdef DS_NO_VIRTUALIZATION
case DS_NO_VIRTUALIZATION:
str = "The buffer was created, but another 3D algorithm was substituted";
break;
return "The buffer was created, but another 3D algorithm was substituted";
#endif
#ifdef DS_INCOMPLETE
case DS_INCOMPLETE:
str = "The method succeeded, but not all the optional effects were obtained";
break;
return "The method succeeded, but not all the optional effects were obtained";
#endif
#ifdef DSERR_ACCESSDENIED
case DSERR_ACCESSDENIED:
str = "The request failed because access was denied";
break;
return "The request failed because access was denied";
#endif
#ifdef 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 "
"by another caller";
break;
#endif
#ifdef DSERR_ALREADYINITIALIZED
case DSERR_ALREADYINITIALIZED:
str = "The object is already initialized";
break;
return "The object is already initialized";
#endif
#ifdef DSERR_BADFORMAT
case DSERR_BADFORMAT:
str = "The specified wave format is not supported";
break;
return "The specified wave format is not supported";
#endif
#ifdef 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";
break;
#endif
#ifdef DSERR_BUFFERLOST
case DSERR_BUFFERLOST:
str = "The buffer memory has been lost and must be restored";
break;
return "The buffer memory has been lost and must be restored";
#endif
#ifdef 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";
break;
#endif
#ifdef 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. "
"Controls must be specified when the buffer is created, "
"using the dwFlags member of DSBUFFERDESC";
break;
#endif
#ifdef 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. "
"For more information, see IDirectSound8 Interface";
break;
#endif
#ifdef 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; "
"for example, an effect expected in hardware "
"was found in software";
break;
#endif
#ifdef DSERR_GENERIC
case DSERR_GENERIC:
str = "An undetermined error occurred inside the DirectSound subsystem";
break;
return "An undetermined error occurred inside the DirectSound subsystem";
#endif
#ifdef DSERR_INVALIDCALL
case DSERR_INVALIDCALL:
str = "This function is not valid for the current state of this object";
break;
return "This function is not valid for the current state of this object";
#endif
#ifdef DSERR_INVALIDPARAM
case DSERR_INVALIDPARAM:
str = "An invalid parameter was passed to the returning function";
break;
return "An invalid parameter was passed to the returning function";
#endif
#ifdef DSERR_NOAGGREGATION
case DSERR_NOAGGREGATION:
str = "The object does not support aggregation";
break;
return "The object does not support aggregation";
#endif
#ifdef 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";
break;
#endif
#ifdef DSERR_NOINTERFACE
case DSERR_NOINTERFACE:
str = "The requested COM interface is not available";
break;
return "The requested COM interface is not available";
#endif
#ifdef DSERR_OBJECTNOTFOUND
case DSERR_OBJECTNOTFOUND:
str = "The requested object was not found";
break;
return "The requested object was not found";
#endif
#ifdef 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";
break;
#endif
#ifdef 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";
break;
#endif
#ifdef DSERR_PRIOLEVELNEEDED
case DSERR_PRIOLEVELNEEDED:
str = "A cooperative level of DSSCL_PRIORITY or higher is required";
break;
return "A cooperative level of DSSCL_PRIORITY or higher is required";
#endif
#ifdef DSERR_SENDLOOP
case DSERR_SENDLOOP:
str = "A circular loop of send effects was detected";
break;
return "A circular loop of send effects was detected";
#endif
#ifdef 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 "
"before other methods were called";
break;
#endif
#ifdef DSERR_UNSUPPORTED
case DSERR_UNSUPPORTED:
str = "The function called is not supported at this time";
break;
return "The function called is not supported at this time";
#endif
default:
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr);
return;
return NULL;
}
}
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);
} else {
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT: 0x%lx)\n", hr);
}
}
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);
}
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)
{
HRESULT hr;
@ -621,7 +592,6 @@ static void dsound_audio_fini (void *opaque)
static void *dsound_audio_init(Audiodev *dev, Error **errp)
{
int err;
HRESULT hr;
dsound *s = g_new0(dsound, 1);
AudiodevDsoundOptions *dso;
@ -637,8 +607,8 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
hr = CoInitialize (NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n");
g_free(s);
dserror_set(errp, hr, "Could not initialize COM");
dsound_audio_fini(s);
return NULL;
}
@ -650,20 +620,15 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
(void **) &s->dsound
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not create DirectSound instance\n");
g_free(s);
dserror_set(errp, hr, "Could not create DirectSound instance");
dsound_audio_fini(s);
return NULL;
}
hr = IDirectSound_Initialize (s->dsound, NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize DirectSound\n");
hr = IDirectSound_Release (s->dsound);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSound\n");
}
g_free(s);
dserror_set(errp, hr, "Could not initialize DirectSound");
dsound_audio_fini(s);
return NULL;
}
@ -675,23 +640,26 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
(void **) &s->dsound_capture
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
} else {
dserror_set(errp, hr, "Could not create DirectSoundCapture instance");
dsound_audio_fini(s);
return NULL;
}
hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
hr = IDirectSoundCapture_Release (s->dsound_capture);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSoundCapture\n");
}
s->dsound_capture = NULL;
}
if (FAILED(hr)) {
dserror_set(errp, hr, "Could not initialize DirectSoundCapture");
dsound_audio_fini(s);
return NULL;
}
err = dsound_set_cooperative_level(s);
if (err) {
dsound_audio_fini (s);
hr = IDirectSound_SetCooperativeLevel (
s->dsound,
GetDesktopWindow(),
DSSCL_PRIORITY
);
if (FAILED(hr)) {
dserror_set(errp, hr, "Could not set cooperative level");
dsound_audio_fini(s);
return NULL;
}
@ -717,7 +685,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
static struct audio_driver dsound_audio_driver = {
.name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.init = dsound_audio_init,
.fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops,

View file

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

View file

@ -1,12 +1,13 @@
system_ss.add([spice_headers, files('audio.c')])
system_ss.add(files(
'audio-hmp-cmds.c',
'audio.c',
'mixeng.c',
'noaudio.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: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))

View file

@ -24,11 +24,13 @@
*/
#include "qemu/osdep.h"
#include "qemu/bswap.h"
#include "qemu/error-report.h"
#include "audio.h"
#include "qemu/audio.h"
#define AUDIO_CAP "mixeng"
#include "audio_int.h"
#ifdef FLOAT_MIXENG
#include "qemu/error-report.h"
#endif
/* 8 bit */
#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)
{
#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");
abort();
#else
const struct st_sample *sample = samples;
sample += pos;
*left = sample->l;
*right = sample->r;
#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)
{
#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");
abort();
#else
struct st_sample *sample = samples;
sample += pos;
sample->l = left;
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 st_sample { int64_t l; int64_t r; };
#endif
typedef struct st_sample st_sample;
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "audio.h"
#include "qemu/audio.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include <spa/param/audio/format-utils.h>
@ -324,7 +324,7 @@ done_unlock:
}
static int
audfmt_to_pw(AudioFormat fmt, int endianness)
audfmt_to_pw(AudioFormat fmt, bool big_endian)
{
int format;
@ -336,19 +336,19 @@ audfmt_to_pw(AudioFormat fmt, int endianness)
format = SPA_AUDIO_FORMAT_U8;
break;
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;
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;
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;
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;
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;
default:
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 = {
.name = "pipewire",
.descr = "http://www.pipewire.org/",
.init = qpw_audio_init,
.fini = qpw_audio_fini,
.pcm_ops = &qpw_pcm_ops,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,7 @@
#include "qemu/osdep.h"
#include "qemu/qemu-print.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "audio.h"
#include "audio_int.h"
typedef struct {
FILE *f;
@ -104,7 +103,7 @@ static struct capture_ops wav_capture_ops = {
.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)
{
WAVState *wav;

View file

@ -53,6 +53,8 @@ typedef struct CryptoDevBackendBuiltinSession {
#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
#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 {
CryptoDevBackend parent_obj;
@ -98,12 +100,7 @@ static void cryptodev_builtin_init(
1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC;
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
/*
* 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_size = CRYPTODEV_BUITLIN_MAX_REQUEST_SIZE;
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
cryptodev_builtin_init_akcipher(backend);

View file

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

View file

@ -46,7 +46,7 @@ struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj;
VhostUserState vhost_user;
CharBackend chr;
CharFrontend chr;
char *chr_name;
bool opened;
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. */
ram_flags = RAM_SHARED;
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),
backend_name, backend->size,

View file

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

View file

@ -11,8 +11,9 @@
#include "qemu/osdep.h"
#include "igvm.h"
#include "qapi/error.h"
#include "qemu/target-info-qapi.h"
#include "system/igvm.h"
#include "system/memory.h"
#include "system/address-spaces.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;
}
/*
* 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,
ctx->current_header_index);
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);
if (ctx->cgs) {
result = ctx->cgsc->set_guest_state(
vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
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);
if (result < 0) {
return result;
@ -543,6 +544,8 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
Error **errp)
{
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;
int max_entry_count;
int entry = 0;
@ -550,7 +553,13 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
ConfidentialGuestMemoryMapEntry cgmm_entry;
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,
"IGVM file contains a memory map but this is not supported "
"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);
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) {
if (entry > max_entry_count) {
if (entry >= max_entry_count) {
error_setg(
errp,
"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;
break;
}
retval =
ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
retval = get_mem_map_entry(++entry, &cgmm_entry, errp);
}
if (retval < 0) {
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,
ram_addr_t size, void *vaddr, bool readonly)
uint64_t size, void *vaddr, bool readonly)
{
int ret, fd = be->fd;
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,
hwaddr iova, ram_addr_t size,
hwaddr iova, uint64_t size,
int mfd, unsigned long start, bool readonly)
{
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,
hwaddr iova, ram_addr_t size)
hwaddr iova, uint64_t size)
{
int ret, fd = be->fd;
struct iommu_ioas_unmap unmap = {

View file

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

View file

@ -13,6 +13,9 @@
#include "qemu/osdep.h"
#include "system/spdm-socket.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,
size_t number_of_bytes)
@ -184,29 +187,61 @@ int spdm_socket_connect(uint16_t port, Error **errp)
return client_socket;
}
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
void *req, uint32_t req_len,
static bool spdm_socket_command_valid(uint32_t command)
{
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)
{
uint32_t command;
bool result;
result = send_platform_data(socket, transport_type,
SPDM_SOCKET_COMMAND_NORMAL,
req, req_len);
if (!result) {
return 0;
}
result = receive_platform_data(socket, transport_type, &command,
(uint8_t *)rsp, &rsp_len);
/* 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) {
return 0;
}
assert(command != 0);
return rsp_len;
return spdm_socket_receive(socket, transport_type, rsp, rsp_len);
}
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,
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;
TPMEmulatorOptions *options;
CharBackend ctrl_chr;
CharFrontend ctrl_chr;
QIOChannel *data_ioc;
TPMVersion tpm_version;
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_out_total)
{
CharBackend *dev = &tpm->ctrl_chr;
CharFrontend *dev = &tpm->ctrl_chr;
uint32_t cmd_no = cpu_to_be32(cmd);
ssize_t n = sizeof(uint32_t) + msg_len_in;
ptm_res res;
@ -308,21 +308,21 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
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);
ptm_res res;
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0,
sizeof(ptm_res), sizeof(res)) < 0) {
error_report("tpm-emulator: Could not stop TPM: %s",
error_setg(errp, "tpm-emulator: Could not stop TPM: %s",
strerror(errno));
return -1;
}
res = be32_to_cpu(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));
return -1;
}
@ -362,12 +362,13 @@ static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu)
static int tpm_emulator_set_buffer_size(TPMBackend *tb,
size_t wanted_size,
size_t *actual_size)
size_t *actual_size,
Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_setbuffersize psbs;
if (tpm_emulator_stop_tpm(tb) < 0) {
if (tpm_emulator_stop_tpm(tb, errp) < 0) {
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,
sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result),
sizeof(psbs.u.resp)) < 0) {
error_report("tpm-emulator: Could not set buffer size: %s",
error_setg(errp, "tpm-emulator: Could not set buffer size: %s",
strerror(errno));
return -1;
}
psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
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,
tpm_emulator_strerror(psbs.u.resp.tpm_result));
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,
bool is_resume)
bool is_resume, Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
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);
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;
}
@ -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),
sizeof(init.u.resp.tpm_result),
sizeof(init)) < 0) {
error_report("tpm-emulator: could not send INIT: %s",
error_setg(errp, "tpm-emulator: could not send INIT: %s",
strerror(errno));
goto err_exit;
}
res = be32_to_cpu(init.u.resp.tpm_result);
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));
goto err_exit;
}
@ -441,18 +443,31 @@ err_exit:
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 */
if (runstate_check(RUN_STATE_INMIGRATE)) {
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 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)
@ -546,7 +561,7 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
{
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;
}
@ -819,7 +834,8 @@ static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu)
static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
uint32_t type,
TPMSizedBuffer *tsb,
uint32_t flags)
uint32_t flags,
Error **errp)
{
ssize_t n;
ptm_setstate pss;
@ -838,15 +854,16 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
/* write the header only */
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
offsetof(ptm_setstate, u.req.data), 0, 0) < 0) {
error_report("tpm-emulator: could not set state blob type %d : %s",
type, strerror(errno));
error_setg_errno(errp, errno,
"tpm-emulator: could not set state blob type %d",
type);
return -1;
}
/* now the body */
n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, 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",
type, tsb->size, n);
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,
(uint8_t *)&pss, sizeof(pss.u.resp));
if (n != sizeof(pss.u.resp)) {
error_report("tpm-emulator: Reading response from writing stateblob "
"(type %d) failed; expected %zu bytes, got %zd", type,
sizeof(pss.u.resp), n);
error_setg(errp, "tpm-emulator: Reading response from writing "
"stateblob (type %d) failed; expected %zu bytes, "
"got %zd", type, sizeof(pss.u.resp), n);
return -1;
}
tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
if (tpm_result != 0) {
error_report("tpm-emulator: Setting the stateblob (type %d) failed "
"with a TPM error 0x%x %s", type, tpm_result,
error_setg(errp, "tpm-emulator: Setting the stateblob (type %d) "
"failed with a TPM error 0x%x %s", type, tpm_result,
tpm_emulator_strerror(tpm_result));
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.
*/
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);
TPMBlobBuffers *state_blobs = &tpm_emu->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");
return -EIO;
}
if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_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,
&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,
&state_blobs->savestate,
state_blobs->savestate_flags) < 0) {
state_blobs->savestate_flags, errp) < 0) {
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.
*
* 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;
int ret;
ret = tpm_emulator_set_state_blobs(tb);
ret = tpm_emulator_set_state_blobs(tb, errp);
if (ret < 0) {
return ret;
return false;
}
if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) {
return -EIO;
if (tpm_emulator_startup_tpm_resume(tb, 0, true, errp) < 0) {
return false;
}
return 0;
return true;
}
static const VMStateDescription vmstate_tpm_emulator = {
.name = "tpm-emulator",
.version_id = 0,
.pre_save = tpm_emulator_pre_save,
.post_load = tpm_emulator_post_load,
.post_load_errp = tpm_emulator_post_load,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(state_blobs.permanent_flags, 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)
{
int fd = -1;
char *dev;
const char *dev;
char path[PATH_MAX];
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,
Error **errp)
{
uint32_t alignment = blk_get_pwrite_zeroes_alignment(blk);
int64_t bytes_to_clear;
int ret;
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) {
ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
if (ret < 0) {
@ -693,7 +694,7 @@ out:
}
int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
Error **errp)
bool allow_protocol_prefix, Error **errp)
{
QemuOpts *protocol_opts;
BlockDriver *drv;
@ -702,7 +703,7 @@ int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
GLOBAL_STATE_CODE();
drv = bdrv_find_protocol(filename, true, errp);
drv = bdrv_find_protocol(filename, allow_protocol_prefix, errp);
if (drv == NULL) {
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,
const char *filename,
bool backing_mask_protocol,
@ -1529,6 +1541,7 @@ const BdrvChildClass child_of_bds = {
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
.change_aio_ctx = bdrv_child_cb_change_aio_ctx,
.resize = bdrv_child_cb_resize,
.update_filename = bdrv_child_cb_update_filename,
.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
* 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
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();
g_autoptr(GSList) refresh_list = NULL;
BlockDriverState *to_cow_parent = NULL;
int ret;
GLOBAL_STATE_CODE();
@ -5405,17 +5414,6 @@ bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
assert(to->quiesce_counter);
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
if (detach_subchain) {
assert(bdrv_chain_contains(from, to));
assert(from != to);
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.
* Replacement may influence the permissions, we should calculate new
@ -5427,11 +5425,6 @@ bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
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, from);
@ -5450,7 +5443,7 @@ out:
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
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)
@ -5466,7 +5459,7 @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
bdrv_drained_begin(child_bs);
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_drained_end(child_bs);
@ -5917,17 +5910,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
updated_children = g_slist_prepend(updated_children, c);
}
/*
* 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_replace_node_common(top, base, false, &local_err);
bdrv_graph_wrunlock();
if (local_err) {

View file

@ -28,6 +28,7 @@
#include "block/block_int.h"
#include "qemu/timer.h"
#include "system/qtest.h"
#include "qapi/error.h"
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
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,
enum OnOffAuto account_failed)
bool block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
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);
stats->account_failed = bool_from_onoffauto(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)

View file

@ -63,9 +63,10 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs,
Coroutine *co)
{
Request *req = g_new(Request, 1);
AioContext *ctx = qemu_coroutine_get_aio_context(co);
*req = (Request) {
.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);
}

View file

@ -1318,9 +1318,9 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
* section.
*/
qemu_mutex_lock(&blk->queued_requests_lock);
/* blk_root_drained_end() has the corresponding blk_inc_in_flight() */
blk_dec_in_flight(blk);
qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock);
blk_inc_in_flight(blk);
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;
}
/* 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 */
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);
}
qemu_mutex_lock(&blk->queued_requests_lock);
while (qemu_co_enter_next(&blk->queued_requests,
&blk->queued_requests_lock)) {
while (!qemu_co_queue_empty(&blk->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);
}

View file

@ -67,11 +67,18 @@ static int block_crypto_read_func(QCryptoBlock *block,
BlockCrypto *crypto = bs->opaque;
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();
GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
}
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read encryption header");
return ret;
@ -90,11 +97,18 @@ static int block_crypto_write_func(QCryptoBlock *block,
BlockCrypto *crypto = bs->opaque;
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();
GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
}
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write encryption header");
return ret;
@ -792,7 +806,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
char *buf = NULL;
int64_t size;
bool detached_hdr =
qemu_opt_get_bool(opts, "detached-header", false);
qemu_opt_get_bool_del(opts, "detached-header", false);
unsigned int cflags = 0;
int ret;
Error *local_err = NULL;
@ -821,7 +835,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
}
/* Create protocol layer */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
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,
void *userp, void *sp)
{
BDRVCURLState *s;
CURLState *state = NULL;
BDRVCURLState *s = userp;
CURLSocket *socket;
curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
s = state->s;
socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
if (!socket) {
socket = g_new0(CURLSocket, 1);
@ -262,8 +258,8 @@ read_end:
}
/* Called with s->mutex held. */
static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
CURLAIOCB *acb)
static bool coroutine_fn
curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, CURLAIOCB *acb)
{
int i;
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++) {
if (!state->acb[j]) {
state->acb[j] = acb;
/* Await ongoing request */
qemu_mutex_unlock(&s->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&s->mutex);
return true;
}
}
@ -382,6 +382,16 @@ static void curl_multi_check_completion(BDRVCURLState *s)
acb->ret = error ? -EIO : 0;
state->acb[i] = NULL;
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);
qemu_mutex_lock(&s->mutex);
}
@ -475,11 +485,11 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
(void *)curl_read_cb) ||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (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_FOLLOWLOCATION, 1) ||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) ||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1L) ||
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;
}
if (s->username) {
@ -520,7 +530,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
goto err;
}
#elif LIBCURL_VERSION_NUM >= 0x071304
#else
if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
goto err;
@ -528,7 +538,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
#endif
#ifdef DEBUG_VERBOSE
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) {
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1L)) {
goto err;
}
#endif
@ -605,6 +615,7 @@ static void curl_attach_aio_context(BlockDriverState *bs,
assert(!s->multi);
s->multi = curl_multi_init();
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_TIMERDATA, s);
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;
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_HEADERDATA, s)) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
@ -824,22 +835,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
goto out;
}
#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) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
"Server didn't report file size.");
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;
@ -882,7 +882,7 @@ out_noclean:
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;
int running;
@ -894,10 +894,13 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
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)) {
goto out;
goto dont_yield;
}
// 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) {
curl_clean_state(state);
acb->ret = -EIO;
goto out;
goto dont_yield;
}
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) {
curl_clean_state(state);
acb->ret = -ENOMEM;
goto out;
goto dont_yield;
}
state->acb[0] = acb;
@ -939,13 +942,16 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
acb->ret = -EIO;
curl_clean_state(state);
goto out;
goto dont_yield;
}
/* Tell curl it needs to kick things off */
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);
}
@ -961,10 +967,7 @@ static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
.bytes = bytes
};
curl_setup_preadv(bs, &acb);
while (acb.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
curl_do_preadv(bs, &acb);
return acb.ret;
}

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