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:
commit
c3d65261db
2322 changed files with 94070 additions and 43853 deletions
|
|
@ -55,7 +55,7 @@ indent_size = 4
|
||||||
emacs_mode = perl
|
emacs_mode = perl
|
||||||
|
|
||||||
# but user kernel "style" for imported scripts
|
# but user kernel "style" for imported scripts
|
||||||
[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}]
|
[scripts/{get_maintainer.pl,checkpatch.pl}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 8
|
indent_size = 8
|
||||||
emacs_mode = perl
|
emacs_mode = perl
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ variables:
|
||||||
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG'
|
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG'
|
||||||
when: never
|
when: never
|
||||||
|
|
||||||
# Scheduled runs on mainline don't get pipelines except for the special Coverity job
|
# Scheduled jobs should explicitly enable the run in their job rules
|
||||||
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_PIPELINE_SOURCE == "schedule"'
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
when: never
|
when: never
|
||||||
|
|
||||||
# Cirrus jobs can't run unless the creds / target repo are set
|
# Cirrus jobs can't run unless the creds / target repo are set
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,18 @@
|
||||||
|
|
||||||
.native_test_job_template:
|
.native_test_job_template:
|
||||||
extends: .common_test_job_template
|
extends: .common_test_job_template
|
||||||
|
before_script:
|
||||||
|
# Prevent logs from the build job that run earlier
|
||||||
|
# from being duplicated in the test job artifacts
|
||||||
|
- rm -f build/meson-logs/*
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||||
when: always
|
when: always
|
||||||
expire_in: 7 days
|
expire_in: 7 days
|
||||||
paths:
|
paths:
|
||||||
- build/meson-logs/testlog.txt
|
- build/meson-logs
|
||||||
reports:
|
reports:
|
||||||
junit: build/meson-logs/testlog.junit.xml
|
junit: build/meson-logs/*.junit.xml
|
||||||
|
|
||||||
.functional_test_job_template:
|
.functional_test_job_template:
|
||||||
extends: .common_test_job_template
|
extends: .common_test_job_template
|
||||||
|
|
@ -104,14 +108,16 @@
|
||||||
when: always
|
when: always
|
||||||
expire_in: 7 days
|
expire_in: 7 days
|
||||||
paths:
|
paths:
|
||||||
- build/tests/results/latest/results.xml
|
- build/meson-logs
|
||||||
- build/tests/results/latest/test-results
|
|
||||||
- build/tests/functional/*/*/*.log
|
- build/tests/functional/*/*/*.log
|
||||||
reports:
|
reports:
|
||||||
junit: build/tests/results/latest/results.xml
|
junit: build/meson-logs/*.junit.xml
|
||||||
before_script:
|
before_script:
|
||||||
- export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1
|
- export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1
|
||||||
- export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache
|
- export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache
|
||||||
|
# Prevent logs from the build job that run earlier
|
||||||
|
# from being duplicated in the test job artifacts
|
||||||
|
- rm -f build/meson-logs/*
|
||||||
after_script:
|
after_script:
|
||||||
- cd build
|
- cd build
|
||||||
- du -chs ${CI_PROJECT_DIR}/*-cache
|
- du -chs ${CI_PROJECT_DIR}/*-cache
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,12 @@ build-system-ubuntu:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-ubuntu2204-container
|
- job: amd64-ubuntu2204-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: ubuntu2204
|
IMAGE: ubuntu2204
|
||||||
CONFIGURE_ARGS: --enable-docs --enable-rust
|
CONFIGURE_ARGS: --enable-docs --enable-rust
|
||||||
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
|
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build check-doc
|
MAKE_CHECK_ARGS: check-build
|
||||||
|
|
||||||
check-system-ubuntu:
|
check-system-ubuntu:
|
||||||
extends: .native_test_job_template
|
extends: .native_test_job_template
|
||||||
|
|
@ -66,7 +66,7 @@ build-system-debian:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-container
|
- job: amd64-debian-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian
|
IMAGE: debian
|
||||||
CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust
|
CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust
|
||||||
|
|
@ -109,7 +109,7 @@ build-system-fedora:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-fedora-container
|
- job: amd64-fedora-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
|
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
|
||||||
|
|
@ -122,7 +122,7 @@ build-system-fedora-rust-nightly:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-fedora-rust-nightly-container
|
- job: amd64-fedora-rust-nightly-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: fedora-rust-nightly
|
IMAGE: fedora-rust-nightly
|
||||||
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
|
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
|
||||||
|
|
@ -167,7 +167,7 @@ build-system-centos:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-centos9-container
|
- job: amd64-centos9-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: centos9
|
IMAGE: centos9
|
||||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server
|
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server
|
||||||
|
|
@ -189,7 +189,7 @@ build-previous-qemu:
|
||||||
- build-previous/tests/qtest/migration-test
|
- build-previous/tests/qtest/migration-test
|
||||||
- build-previous/scripts
|
- build-previous/scripts
|
||||||
needs:
|
needs:
|
||||||
job: amd64-opensuse-leap-container
|
- job: amd64-opensuse-leap-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: opensuse-leap
|
IMAGE: opensuse-leap
|
||||||
TARGETS: x86_64-softmmu aarch64-softmmu
|
TARGETS: x86_64-softmmu aarch64-softmmu
|
||||||
|
|
@ -274,7 +274,7 @@ build-system-opensuse:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-opensuse-leap-container
|
- job: amd64-opensuse-leap-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: opensuse-leap
|
IMAGE: opensuse-leap
|
||||||
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
|
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
|
||||||
|
|
@ -308,7 +308,7 @@ build-system-flaky:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-container
|
- job: amd64-debian-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian
|
IMAGE: debian
|
||||||
QEMU_JOB_OPTIONAL: 1
|
QEMU_JOB_OPTIONAL: 1
|
||||||
|
|
@ -338,7 +338,7 @@ functional-system-flaky:
|
||||||
build-tcg-disabled:
|
build-tcg-disabled:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-centos9-container
|
- job: amd64-centos9-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: centos9
|
IMAGE: centos9
|
||||||
script:
|
script:
|
||||||
|
|
@ -364,7 +364,7 @@ build-tcg-disabled:
|
||||||
build-user:
|
build-user:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-user-cross-container
|
- job: amd64-debian-user-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-all-test-cross
|
IMAGE: debian-all-test-cross
|
||||||
CONFIGURE_ARGS: --disable-tools --disable-system
|
CONFIGURE_ARGS: --disable-tools --disable-system
|
||||||
|
|
@ -374,7 +374,7 @@ build-user:
|
||||||
build-user-static:
|
build-user-static:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-user-cross-container
|
- job: amd64-debian-user-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-all-test-cross
|
IMAGE: debian-all-test-cross
|
||||||
CONFIGURE_ARGS: --disable-tools --disable-system --static
|
CONFIGURE_ARGS: --disable-tools --disable-system --static
|
||||||
|
|
@ -385,7 +385,7 @@ build-user-static:
|
||||||
build-legacy:
|
build-legacy:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-legacy-cross-container
|
- job: amd64-debian-legacy-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-legacy-test-cross
|
IMAGE: debian-legacy-test-cross
|
||||||
TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user
|
TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user
|
||||||
|
|
@ -395,7 +395,7 @@ build-legacy:
|
||||||
build-user-hexagon:
|
build-user-hexagon:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: hexagon-cross-container
|
- job: hexagon-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-hexagon-cross
|
IMAGE: debian-hexagon-cross
|
||||||
TARGETS: hexagon-linux-user
|
TARGETS: hexagon-linux-user
|
||||||
|
|
@ -408,7 +408,7 @@ build-user-hexagon:
|
||||||
build-some-softmmu:
|
build-some-softmmu:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-user-cross-container
|
- job: amd64-debian-user-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-all-test-cross
|
IMAGE: debian-all-test-cross
|
||||||
CONFIGURE_ARGS: --disable-tools --enable-debug
|
CONFIGURE_ARGS: --disable-tools --enable-debug
|
||||||
|
|
@ -419,7 +419,7 @@ build-some-softmmu:
|
||||||
build-loongarch64:
|
build-loongarch64:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: loongarch-debian-cross-container
|
- job: loongarch-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-loongarch-cross
|
IMAGE: debian-loongarch-cross
|
||||||
CONFIGURE_ARGS: --disable-tools --enable-debug
|
CONFIGURE_ARGS: --disable-tools --enable-debug
|
||||||
|
|
@ -430,7 +430,7 @@ build-loongarch64:
|
||||||
build-tricore-softmmu:
|
build-tricore-softmmu:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: tricore-debian-cross-container
|
- job: tricore-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-tricore-cross
|
IMAGE: debian-tricore-cross
|
||||||
CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug
|
CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug
|
||||||
|
|
@ -440,7 +440,7 @@ build-tricore-softmmu:
|
||||||
clang-system:
|
clang-system:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-fedora-container
|
- job: amd64-fedora-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
|
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
|
||||||
|
|
@ -451,7 +451,7 @@ clang-system:
|
||||||
clang-user:
|
clang-user:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-user-cross-container
|
- job: amd64-debian-user-cross-container
|
||||||
timeout: 70m
|
timeout: 70m
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-all-test-cross
|
IMAGE: debian-all-test-cross
|
||||||
|
|
@ -479,7 +479,7 @@ build-cfi-aarch64:
|
||||||
LD_JOBS: 1
|
LD_JOBS: 1
|
||||||
AR: llvm-ar
|
AR: llvm-ar
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
|
||||||
--enable-safe-stack --disable-slirp
|
--enable-safe-stack --disable-slirp
|
||||||
TARGETS: aarch64-softmmu
|
TARGETS: aarch64-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build
|
MAKE_CHECK_ARGS: check-build
|
||||||
|
|
@ -517,7 +517,7 @@ build-cfi-ppc64-s390x:
|
||||||
LD_JOBS: 1
|
LD_JOBS: 1
|
||||||
AR: llvm-ar
|
AR: llvm-ar
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
|
||||||
--enable-safe-stack --disable-slirp
|
--enable-safe-stack --disable-slirp
|
||||||
TARGETS: ppc64-softmmu s390x-softmmu
|
TARGETS: ppc64-softmmu s390x-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build
|
MAKE_CHECK_ARGS: check-build
|
||||||
|
|
@ -555,7 +555,7 @@ build-cfi-x86_64:
|
||||||
LD_JOBS: 1
|
LD_JOBS: 1
|
||||||
AR: llvm-ar
|
AR: llvm-ar
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
|
||||||
--enable-safe-stack --disable-slirp
|
--enable-safe-stack --disable-slirp
|
||||||
TARGETS: x86_64-softmmu
|
TARGETS: x86_64-softmmu
|
||||||
MAKE_CHECK_ARGS: check-build
|
MAKE_CHECK_ARGS: check-build
|
||||||
|
|
@ -582,7 +582,7 @@ functional-cfi-x86_64:
|
||||||
tsan-build:
|
tsan-build:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-ubuntu2204-container
|
- job: amd64-ubuntu2204-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: ubuntu2204
|
IMAGE: ubuntu2204
|
||||||
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
|
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
|
||||||
|
|
@ -596,7 +596,7 @@ tsan-build:
|
||||||
gcov:
|
gcov:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-ubuntu2204-container
|
- job: amd64-ubuntu2204-container
|
||||||
timeout: 80m
|
timeout: 80m
|
||||||
variables:
|
variables:
|
||||||
IMAGE: ubuntu2204
|
IMAGE: ubuntu2204
|
||||||
|
|
@ -613,9 +613,9 @@ gcov:
|
||||||
when: always
|
when: always
|
||||||
expire_in: 2 days
|
expire_in: 2 days
|
||||||
paths:
|
paths:
|
||||||
- build/meson-logs/testlog.txt
|
- build/meson-logs
|
||||||
reports:
|
reports:
|
||||||
junit: build/meson-logs/testlog.junit.xml
|
junit: build/meson-logs/*.junit.xml
|
||||||
coverage_report:
|
coverage_report:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: build/coverage.xml
|
path: build/coverage.xml
|
||||||
|
|
@ -623,7 +623,7 @@ gcov:
|
||||||
build-oss-fuzz:
|
build-oss-fuzz:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-fedora-container
|
- job: amd64-fedora-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
script:
|
script:
|
||||||
|
|
@ -645,7 +645,7 @@ build-oss-fuzz:
|
||||||
build-tci:
|
build-tci:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-user-cross-container
|
- job: amd64-debian-user-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-all-test-cross
|
IMAGE: debian-all-test-cross
|
||||||
script:
|
script:
|
||||||
|
|
@ -656,21 +656,19 @@ build-tci:
|
||||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
||||||
|| { cat config.log meson-logs/meson-log.txt && exit 1; }
|
|| { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||||
- make -j"$JOBS"
|
- make -j"$JOBS"
|
||||||
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test
|
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test
|
||||||
- for tg in $TARGETS ; do
|
- for tg in $TARGETS ; do
|
||||||
export QTEST_QEMU_BINARY="./qemu-system-${tg}" ;
|
export QTEST_QEMU_BINARY="./qemu-system-${tg}" ;
|
||||||
./tests/qtest/boot-serial-test || exit 1 ;
|
./tests/qtest/boot-serial-test || exit 1 ;
|
||||||
./tests/qtest/cdrom-test || exit 1 ;
|
./tests/qtest/cdrom-test || exit 1 ;
|
||||||
done
|
done
|
||||||
- QTEST_QEMU_BINARY="./qemu-system-x86_64" ./tests/qtest/pxe-test
|
|
||||||
- QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow
|
|
||||||
- make check-tcg
|
- make check-tcg
|
||||||
|
|
||||||
# Check our reduced build configurations
|
# Check our reduced build configurations
|
||||||
build-without-defaults:
|
build-without-defaults:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-centos9-container
|
- job: amd64-centos9-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: centos9
|
IMAGE: centos9
|
||||||
CONFIGURE_ARGS:
|
CONFIGURE_ARGS:
|
||||||
|
|
@ -688,7 +686,7 @@ build-libvhost-user:
|
||||||
stage: build
|
stage: build
|
||||||
image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG
|
image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG
|
||||||
needs:
|
needs:
|
||||||
job: amd64-fedora-container
|
- job: amd64-fedora-container
|
||||||
script:
|
script:
|
||||||
- mkdir subprojects/libvhost-user/build
|
- mkdir subprojects/libvhost-user/build
|
||||||
- cd subprojects/libvhost-user/build
|
- cd subprojects/libvhost-user/build
|
||||||
|
|
@ -702,7 +700,7 @@ build-tools-and-docs-debian:
|
||||||
- .native_build_job_template
|
- .native_build_job_template
|
||||||
- .native_build_artifact_template
|
- .native_build_artifact_template
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-container
|
- job: amd64-debian-container
|
||||||
# when running on 'master' we use pre-existing container
|
# when running on 'master' we use pre-existing container
|
||||||
optional: true
|
optional: true
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -736,7 +734,7 @@ pages:
|
||||||
- make gtags
|
- make gtags
|
||||||
# We unset variables to work around a bug in some htags versions
|
# We unset variables to work around a bug in some htags versions
|
||||||
# which causes it to fail when the environment is large
|
# which causes it to fail when the environment is large
|
||||||
- CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags
|
- CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= CI_COMMIT_DESCRIPTION= htags
|
||||||
-anT --tree-view=filetree -m qemu_init
|
-anT --tree-view=filetree -m qemu_init
|
||||||
-t "Welcome to the QEMU sourcecode"
|
-t "Welcome to the QEMU sourcecode"
|
||||||
- mv HTML public/src
|
- mv HTML public/src
|
||||||
|
|
@ -759,7 +757,7 @@ coverity:
|
||||||
- job: amd64-fedora-container
|
- job: amd64-fedora-container
|
||||||
optional: true
|
optional: true
|
||||||
before_script:
|
before_script:
|
||||||
- dnf install -y curl wget
|
- dnf install -y curl wget file
|
||||||
script:
|
script:
|
||||||
# would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089)
|
# would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089)
|
||||||
# for example:
|
# for example:
|
||||||
|
|
@ -791,7 +789,7 @@ build-wasm:
|
||||||
extends: .wasm_build_job_template
|
extends: .wasm_build_job_template
|
||||||
timeout: 2h
|
timeout: 2h
|
||||||
needs:
|
needs:
|
||||||
job: wasm-emsdk-cross-container
|
- job: wasm-emsdk-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: emsdk-wasm32-cross
|
IMAGE: emsdk-wasm32-cross
|
||||||
CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter
|
CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@ x64-freebsd-14-build:
|
||||||
NAME: freebsd-14
|
NAME: freebsd-14
|
||||||
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
|
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
|
||||||
CIRRUS_VM_IMAGE_SELECTOR: image_family
|
CIRRUS_VM_IMAGE_SELECTOR: image_family
|
||||||
CIRRUS_VM_IMAGE_NAME: freebsd-14-2
|
CIRRUS_VM_IMAGE_NAME: freebsd-14-3
|
||||||
CIRRUS_VM_CPUS: 8
|
CIRRUS_VM_CPUS: 8
|
||||||
CIRRUS_VM_RAM: 8G
|
CIRRUS_VM_RAM: 8G
|
||||||
UPDATE_COMMAND: pkg update; pkg upgrade -y
|
UPDATE_COMMAND: pkg update; pkg upgrade -y
|
||||||
INSTALL_COMMAND: pkg install -y
|
INSTALL_COMMAND: pkg install -y
|
||||||
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu
|
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu --enable-rust
|
||||||
TEST_TARGETS: check
|
TEST_TARGETS: check
|
||||||
|
|
||||||
aarch64-macos-build:
|
aarch64-macos-build:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake'
|
||||||
NINJA='/usr/local/bin/ninja'
|
NINJA='/usr/local/bin/ninja'
|
||||||
PACKAGING_COMMAND='pkg'
|
PACKAGING_COMMAND='pkg'
|
||||||
PIP3='/usr/local/bin/pip'
|
PIP3='/usr/local/bin/pip'
|
||||||
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 vulkan-tools xorriso zstd'
|
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache4 cmocka coreutils ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 vulkan-tools xorriso zstd'
|
||||||
PYPI_PKGS=''
|
PYPI_PKGS=''
|
||||||
PYTHON='/usr/local/bin/python3'
|
PYTHON='/usr/local/bin/python3'
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake'
|
||||||
NINJA='/opt/homebrew/bin/ninja'
|
NINJA='/opt/homebrew/bin/ninja'
|
||||||
PACKAGING_COMMAND='brew'
|
PACKAGING_COMMAND='brew'
|
||||||
PIP3='/opt/homebrew/bin/pip3'
|
PIP3='/opt/homebrew/bin/pip3'
|
||||||
PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd'
|
PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka coreutils ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd'
|
||||||
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
|
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
|
||||||
PYTHON='/opt/homebrew/bin/python3'
|
PYTHON='/opt/homebrew/bin/python3'
|
||||||
|
|
|
||||||
|
|
@ -52,12 +52,6 @@ mips64el-debian-cross-container:
|
||||||
variables:
|
variables:
|
||||||
NAME: debian-mips64el-cross
|
NAME: debian-mips64el-cross
|
||||||
|
|
||||||
mipsel-debian-cross-container:
|
|
||||||
extends: .container_job_template
|
|
||||||
stage: containers
|
|
||||||
variables:
|
|
||||||
NAME: debian-mipsel-cross
|
|
||||||
|
|
||||||
ppc64el-debian-cross-container:
|
ppc64el-debian-cross-container:
|
||||||
extends: .container_job_template
|
extends: .container_job_template
|
||||||
stage: containers
|
stage: containers
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,7 @@
|
||||||
- docker push "$TAG"
|
- docker push "$TAG"
|
||||||
after_script:
|
after_script:
|
||||||
- docker logout
|
- docker logout
|
||||||
|
rules:
|
||||||
|
# because we want to enable this for scheduled runs we also have to replicate the normal rules
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM'
|
||||||
|
- !reference [.base_job_template, rules]
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,42 @@ amd64-fedora-rust-nightly-container:
|
||||||
variables:
|
variables:
|
||||||
NAME: fedora-rust-nightly
|
NAME: fedora-rust-nightly
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
|
# this scheduled job will trigger all the containers to build
|
||||||
|
weekly-container-builds:
|
||||||
|
extends: .container_job_template
|
||||||
|
allow_failure: true
|
||||||
|
needs:
|
||||||
|
# core
|
||||||
|
- amd64-centos9-container
|
||||||
|
- amd64-fedora-container
|
||||||
|
# cross
|
||||||
|
- amd64-debian-cross-container
|
||||||
|
- amd64-debian-user-cross-container
|
||||||
|
- amd64-debian-legacy-cross-container
|
||||||
|
- arm64-debian-cross-container
|
||||||
|
- armhf-debian-cross-container
|
||||||
|
- hexagon-cross-container
|
||||||
|
- loongarch-debian-cross-container
|
||||||
|
- i686-debian-cross-container
|
||||||
|
- mips64el-debian-cross-container
|
||||||
|
- ppc64el-debian-cross-container
|
||||||
|
- riscv64-debian-cross-container
|
||||||
|
- s390x-debian-cross-container
|
||||||
|
- tricore-debian-cross-container
|
||||||
|
- xtensa-debian-cross-container
|
||||||
|
- win64-fedora-cross-container
|
||||||
|
- wasm-emsdk-cross-container
|
||||||
|
# containers
|
||||||
|
- amd64-alpine-container
|
||||||
|
- amd64-debian-container
|
||||||
|
- amd64-ubuntu2204-container
|
||||||
|
- amd64-opensuse-leap-container
|
||||||
|
- python-container
|
||||||
|
- amd64-fedora-rust-nightly-container
|
||||||
|
script:
|
||||||
|
- apk -U add make bash skopeo
|
||||||
|
- make docker-verify V=1 DOCKER_DEFAULT_REGISTRY=$CI_REGISTRY_IMAGE
|
||||||
|
rules:
|
||||||
|
# this only ever runes as a scheduled build
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,6 @@
|
||||||
when: always
|
when: always
|
||||||
expire_in: 7 days
|
expire_in: 7 days
|
||||||
paths:
|
paths:
|
||||||
- build/meson-logs/testlog.txt
|
- build/meson-logs
|
||||||
reports:
|
reports:
|
||||||
junit: build/meson-logs/testlog.junit.xml
|
junit: build/meson-logs/*.junit.xml
|
||||||
|
|
|
||||||
|
|
@ -4,28 +4,28 @@ include:
|
||||||
cross-armhf-user:
|
cross-armhf-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
needs:
|
needs:
|
||||||
job: armhf-debian-cross-container
|
- job: armhf-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-armhf-cross
|
IMAGE: debian-armhf-cross
|
||||||
|
|
||||||
cross-arm64-system:
|
cross-arm64-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
needs:
|
needs:
|
||||||
job: arm64-debian-cross-container
|
- job: arm64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-arm64-cross
|
IMAGE: debian-arm64-cross
|
||||||
|
|
||||||
cross-arm64-user:
|
cross-arm64-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
needs:
|
needs:
|
||||||
job: arm64-debian-cross-container
|
- job: arm64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-arm64-cross
|
IMAGE: debian-arm64-cross
|
||||||
|
|
||||||
cross-arm64-kvm-only:
|
cross-arm64-kvm-only:
|
||||||
extends: .cross_accel_build_job
|
extends: .cross_accel_build_job
|
||||||
needs:
|
needs:
|
||||||
job: arm64-debian-cross-container
|
- job: arm64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-arm64-cross
|
IMAGE: debian-arm64-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features
|
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features
|
||||||
|
|
@ -35,7 +35,7 @@ cross-i686-system:
|
||||||
- .cross_system_build_job
|
- .cross_system_build_job
|
||||||
- .cross_test_artifacts
|
- .cross_test_artifacts
|
||||||
needs:
|
needs:
|
||||||
job: i686-debian-cross-container
|
- job: i686-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-i686-cross
|
IMAGE: debian-i686-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --disable-kvm
|
EXTRA_CONFIGURE_OPTS: --disable-kvm
|
||||||
|
|
@ -46,7 +46,7 @@ cross-i686-user:
|
||||||
- .cross_user_build_job
|
- .cross_user_build_job
|
||||||
- .cross_test_artifacts
|
- .cross_test_artifacts
|
||||||
needs:
|
needs:
|
||||||
job: i686-debian-cross-container
|
- job: i686-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-i686-cross
|
IMAGE: debian-i686-cross
|
||||||
MAKE_CHECK_ARGS: check
|
MAKE_CHECK_ARGS: check
|
||||||
|
|
@ -57,7 +57,7 @@ cross-i686-tci:
|
||||||
- .cross_test_artifacts
|
- .cross_test_artifacts
|
||||||
timeout: 60m
|
timeout: 60m
|
||||||
needs:
|
needs:
|
||||||
job: i686-debian-cross-container
|
- job: i686-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-i686-cross
|
IMAGE: debian-i686-cross
|
||||||
ACCEL: tcg-interpreter
|
ACCEL: tcg-interpreter
|
||||||
|
|
@ -68,52 +68,38 @@ cross-i686-tci:
|
||||||
# would otherwise be using a parallelism of 9.
|
# would otherwise be using a parallelism of 9.
|
||||||
MAKE_CHECK_ARGS: check check-tcg -j2
|
MAKE_CHECK_ARGS: check check-tcg -j2
|
||||||
|
|
||||||
cross-mipsel-system:
|
|
||||||
extends: .cross_system_build_job
|
|
||||||
needs:
|
|
||||||
job: mipsel-debian-cross-container
|
|
||||||
variables:
|
|
||||||
IMAGE: debian-mipsel-cross
|
|
||||||
|
|
||||||
cross-mipsel-user:
|
|
||||||
extends: .cross_user_build_job
|
|
||||||
needs:
|
|
||||||
job: mipsel-debian-cross-container
|
|
||||||
variables:
|
|
||||||
IMAGE: debian-mipsel-cross
|
|
||||||
|
|
||||||
cross-mips64el-system:
|
cross-mips64el-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
needs:
|
needs:
|
||||||
job: mips64el-debian-cross-container
|
- job: mips64el-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-mips64el-cross
|
IMAGE: debian-mips64el-cross
|
||||||
|
|
||||||
cross-mips64el-user:
|
cross-mips64el-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
needs:
|
needs:
|
||||||
job: mips64el-debian-cross-container
|
- job: mips64el-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-mips64el-cross
|
IMAGE: debian-mips64el-cross
|
||||||
|
|
||||||
cross-ppc64el-system:
|
cross-ppc64el-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
needs:
|
needs:
|
||||||
job: ppc64el-debian-cross-container
|
- job: ppc64el-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-ppc64el-cross
|
IMAGE: debian-ppc64el-cross
|
||||||
|
|
||||||
cross-ppc64el-user:
|
cross-ppc64el-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
needs:
|
needs:
|
||||||
job: ppc64el-debian-cross-container
|
- job: ppc64el-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-ppc64el-cross
|
IMAGE: debian-ppc64el-cross
|
||||||
|
|
||||||
cross-ppc64el-kvm-only:
|
cross-ppc64el-kvm-only:
|
||||||
extends: .cross_accel_build_job
|
extends: .cross_accel_build_job
|
||||||
needs:
|
needs:
|
||||||
job: ppc64el-debian-cross-container
|
- job: ppc64el-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-ppc64el-cross
|
IMAGE: debian-ppc64el-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
|
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
|
||||||
|
|
@ -121,35 +107,35 @@ cross-ppc64el-kvm-only:
|
||||||
cross-riscv64-system:
|
cross-riscv64-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
needs:
|
needs:
|
||||||
job: riscv64-debian-cross-container
|
- job: riscv64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-riscv64-cross
|
IMAGE: debian-riscv64-cross
|
||||||
|
|
||||||
cross-riscv64-user:
|
cross-riscv64-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
needs:
|
needs:
|
||||||
job: riscv64-debian-cross-container
|
- job: riscv64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-riscv64-cross
|
IMAGE: debian-riscv64-cross
|
||||||
|
|
||||||
cross-s390x-system:
|
cross-s390x-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
needs:
|
needs:
|
||||||
job: s390x-debian-cross-container
|
- job: s390x-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-s390x-cross
|
IMAGE: debian-s390x-cross
|
||||||
|
|
||||||
cross-s390x-user:
|
cross-s390x-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
needs:
|
needs:
|
||||||
job: s390x-debian-cross-container
|
- job: s390x-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-s390x-cross
|
IMAGE: debian-s390x-cross
|
||||||
|
|
||||||
cross-s390x-kvm-only:
|
cross-s390x-kvm-only:
|
||||||
extends: .cross_accel_build_job
|
extends: .cross_accel_build_job
|
||||||
needs:
|
needs:
|
||||||
job: s390x-debian-cross-container
|
- job: s390x-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-s390x-cross
|
IMAGE: debian-s390x-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace
|
EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace
|
||||||
|
|
@ -157,7 +143,7 @@ cross-s390x-kvm-only:
|
||||||
cross-mips64el-kvm-only:
|
cross-mips64el-kvm-only:
|
||||||
extends: .cross_accel_build_job
|
extends: .cross_accel_build_job
|
||||||
needs:
|
needs:
|
||||||
job: mips64el-debian-cross-container
|
- job: mips64el-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-mips64el-cross
|
IMAGE: debian-mips64el-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu
|
EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu
|
||||||
|
|
@ -165,7 +151,7 @@ cross-mips64el-kvm-only:
|
||||||
cross-win64-system:
|
cross-win64-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
needs:
|
needs:
|
||||||
job: win64-fedora-cross-container
|
- job: win64-fedora-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: fedora-win64-cross
|
IMAGE: fedora-win64-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins
|
EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins
|
||||||
|
|
@ -181,7 +167,7 @@ cross-win64-system:
|
||||||
cross-amd64-xen-only:
|
cross-amd64-xen-only:
|
||||||
extends: .cross_accel_build_job
|
extends: .cross_accel_build_job
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-cross-container
|
- job: amd64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-amd64-cross
|
IMAGE: debian-amd64-cross
|
||||||
ACCEL: xen
|
ACCEL: xen
|
||||||
|
|
@ -190,7 +176,7 @@ cross-amd64-xen-only:
|
||||||
cross-arm64-xen-only:
|
cross-arm64-xen-only:
|
||||||
extends: .cross_accel_build_job
|
extends: .cross_accel_build_job
|
||||||
needs:
|
needs:
|
||||||
job: arm64-debian-cross-container
|
- job: arm64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
IMAGE: debian-arm64-cross
|
IMAGE: debian-arm64-cross
|
||||||
ACCEL: xen
|
ACCEL: xen
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
- build/build.ninja
|
- build/build.ninja
|
||||||
- build/meson-logs
|
- build/meson-logs
|
||||||
reports:
|
reports:
|
||||||
junit: build/meson-logs/testlog.junit.xml
|
junit: build/meson-logs/*.junit.xml
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml'
|
- local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml'
|
||||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
|
- local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml'
|
||||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml'
|
- local: '/.gitlab-ci.d/custom-runners/debian-13-ppc64le.yml'
|
||||||
|
|
|
||||||
45
.gitlab-ci.d/custom-runners/debian-13-ppc64le.yml
Normal file
45
.gitlab-ci.d/custom-runners/debian-13-ppc64le.yml
Normal 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
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
90
.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml
Normal file
90
.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml
Normal 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
|
||||||
89
.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml
Normal file
89
.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml
Normal 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
|
||||||
|
|
@ -32,7 +32,7 @@ check-python-minreqs:
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: 1
|
GIT_DEPTH: 1
|
||||||
needs:
|
needs:
|
||||||
job: python-container
|
- job: python-container
|
||||||
|
|
||||||
check-python-tox:
|
check-python-tox:
|
||||||
extends: .base_job_template
|
extends: .base_job_template
|
||||||
|
|
@ -45,7 +45,7 @@ check-python-tox:
|
||||||
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
|
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
|
||||||
QEMU_JOB_OPTIONAL: 1
|
QEMU_JOB_OPTIONAL: 1
|
||||||
needs:
|
needs:
|
||||||
job: python-container
|
- job: python-container
|
||||||
|
|
||||||
check-rust-tools-nightly:
|
check-rust-tools-nightly:
|
||||||
extends: .base_job_template
|
extends: .base_job_template
|
||||||
|
|
@ -76,7 +76,7 @@ check-build-units:
|
||||||
stage: build
|
stage: build
|
||||||
image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG
|
image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG
|
||||||
needs:
|
needs:
|
||||||
job: amd64-debian-container
|
- job: amd64-debian-container
|
||||||
before_script:
|
before_script:
|
||||||
- source scripts/ci/gitlab-ci-section
|
- source scripts/ci/gitlab-ci-section
|
||||||
- section_start setup "Install Tools"
|
- section_start setup "Install Tools"
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,10 @@ msys2-64bit:
|
||||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||||
expire_in: 7 days
|
expire_in: 7 days
|
||||||
paths:
|
paths:
|
||||||
- build/meson-logs/testlog.txt
|
- build/meson-logs
|
||||||
|
- build/cache-log.txt
|
||||||
reports:
|
reports:
|
||||||
junit: "build/meson-logs/testlog.junit.xml"
|
junit: build/meson-logs/*.junit.xml
|
||||||
before_script:
|
before_script:
|
||||||
- Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)"
|
- Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)"
|
||||||
- If ( !(Test-Path -Path msys64\var\cache ) ) {
|
- If ( !(Test-Path -Path msys64\var\cache ) ) {
|
||||||
|
|
@ -77,7 +78,7 @@ msys2-64bit:
|
||||||
git grep make sed
|
git grep make sed
|
||||||
mingw-w64-x86_64-binutils
|
mingw-w64-x86_64-binutils
|
||||||
mingw-w64-x86_64-ccache
|
mingw-w64-x86_64-ccache
|
||||||
mingw-w64-x86_64-curl
|
mingw-w64-x86_64-curl-winssl
|
||||||
mingw-w64-x86_64-gcc
|
mingw-w64-x86_64-gcc
|
||||||
mingw-w64-x86_64-glib2
|
mingw-w64-x86_64-glib2
|
||||||
mingw-w64-x86_64-libnfs
|
mingw-w64-x86_64-libnfs
|
||||||
|
|
@ -87,13 +88,14 @@ msys2-64bit:
|
||||||
mingw-w64-x86_64-pkgconf
|
mingw-w64-x86_64-pkgconf
|
||||||
mingw-w64-x86_64-python
|
mingw-w64-x86_64-python
|
||||||
mingw-w64-x86_64-zstd"
|
mingw-w64-x86_64-zstd"
|
||||||
|
- .\msys64\usr\bin\bash -lc "pacman -Sc --noconfirm"
|
||||||
- Write-Output "Running build at $(Get-Date -Format u)"
|
- Write-Output "Running build at $(Get-Date -Format u)"
|
||||||
- $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc)
|
- $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc)
|
||||||
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
|
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
|
||||||
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
|
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
|
||||||
- $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR"
|
- $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR"
|
||||||
- $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache"
|
- $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache"
|
||||||
- $env:CCACHE_MAXSIZE = "500M"
|
- $env:CCACHE_MAXSIZE = "180M"
|
||||||
- $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode
|
- $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode
|
||||||
- $env:CC = "ccache gcc"
|
- $env:CC = "ccache gcc"
|
||||||
- mkdir build
|
- mkdir build
|
||||||
|
|
@ -102,5 +104,7 @@ msys2-64bit:
|
||||||
- ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS"
|
- ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS"
|
||||||
- ..\msys64\usr\bin\bash -lc "make -j$env:JOBS"
|
- ..\msys64\usr\bin\bash -lc "make -j$env:JOBS"
|
||||||
- ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;"
|
- ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;"
|
||||||
|
- ..\msys64\usr\bin\bash -lc "ls -lR /var/cache > cache-log.txt"
|
||||||
|
- ..\msys64\usr\bin\bash -lc "du -sh ."
|
||||||
- ..\msys64\usr\bin\bash -lc "ccache --show-stats"
|
- ..\msys64\usr\bin\bash -lc "ccache --show-stats"
|
||||||
- Write-Output "Finished build at $(Get-Date -Format u)"
|
- Write-Output "Finished build at $(Get-Date -Format u)"
|
||||||
|
|
|
||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -15,6 +15,7 @@
|
||||||
url = https://gitlab.com/qemu-project/qemu-palcode.git
|
url = https://gitlab.com/qemu-project/qemu-palcode.git
|
||||||
[submodule "roms/u-boot"]
|
[submodule "roms/u-boot"]
|
||||||
path = roms/u-boot
|
path = roms/u-boot
|
||||||
|
# upstream is https://github.com/u-boot/u-boot
|
||||||
url = https://gitlab.com/qemu-project/u-boot.git
|
url = https://gitlab.com/qemu-project/u-boot.git
|
||||||
[submodule "roms/skiboot"]
|
[submodule "roms/skiboot"]
|
||||||
path = roms/skiboot
|
path = roms/skiboot
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
url = https://gitlab.com/qemu-project/seabios-hppa.git
|
url = https://gitlab.com/qemu-project/seabios-hppa.git
|
||||||
[submodule "roms/u-boot-sam460ex"]
|
[submodule "roms/u-boot-sam460ex"]
|
||||||
path = roms/u-boot-sam460ex
|
path = roms/u-boot-sam460ex
|
||||||
|
# upstream is https://github.com/zbalaton/u-boot-sam460ex
|
||||||
url = https://gitlab.com/qemu-project/u-boot-sam460ex.git
|
url = https://gitlab.com/qemu-project/u-boot-sam460ex.git
|
||||||
[submodule "roms/edk2"]
|
[submodule "roms/edk2"]
|
||||||
path = roms/edk2
|
path = roms/edk2
|
||||||
|
|
|
||||||
16
.gitpublish
16
.gitpublish
|
|
@ -4,48 +4,48 @@
|
||||||
# See https://github.com/stefanha/git-publish for more information
|
# See https://github.com/stefanha/git-publish for more information
|
||||||
#
|
#
|
||||||
[gitpublishprofile "default"]
|
[gitpublishprofile "default"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "rfc"]
|
[gitpublishprofile "rfc"]
|
||||||
base = master
|
base = origin/master
|
||||||
prefix = RFC PATCH
|
prefix = RFC PATCH
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "stable"]
|
[gitpublishprofile "stable"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cc = qemu-stable@nongnu.org
|
cc = qemu-stable@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "trivial"]
|
[gitpublishprofile "trivial"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cc = qemu-trivial@nongnu.org
|
cc = qemu-trivial@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "block"]
|
[gitpublishprofile "block"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cc = qemu-block@nongnu.org
|
cc = qemu-block@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "arm"]
|
[gitpublishprofile "arm"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cc = qemu-arm@nongnu.org
|
cc = qemu-arm@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "s390"]
|
[gitpublishprofile "s390"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cc = qemu-s390@nongnu.org
|
cc = qemu-s390@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
||||||
[gitpublishprofile "ppc"]
|
[gitpublishprofile "ppc"]
|
||||||
base = master
|
base = origin/master
|
||||||
to = qemu-devel@nongnu.org
|
to = qemu-devel@nongnu.org
|
||||||
cc = qemu-ppc@nongnu.org
|
cc = qemu-ppc@nongnu.org
|
||||||
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
|
||||||
|
|
|
||||||
3
.mailmap
3
.mailmap
|
|
@ -74,6 +74,7 @@ Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgte
|
||||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
|
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
|
||||||
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
|
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
|
||||||
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
|
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
|
||||||
|
Alex Williamson <alex@shazbot.org> <alex.williamson@redhat.com>
|
||||||
Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
|
Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
|
||||||
Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca>
|
Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca>
|
||||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||||
|
|
@ -81,6 +82,7 @@ Brian Cain <brian.cain@oss.qualcomm.com> <bcain@quicinc.com>
|
||||||
Brian Cain <brian.cain@oss.qualcomm.com> <quic_bcain@quicinc.com>
|
Brian Cain <brian.cain@oss.qualcomm.com> <quic_bcain@quicinc.com>
|
||||||
Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.com>
|
Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.com>
|
||||||
Damien Hedde <damien.hedde@dahe.fr> <damien.hedde@greensocs.com>
|
Damien Hedde <damien.hedde@dahe.fr> <damien.hedde@greensocs.com>
|
||||||
|
David Hildenbrand <david@kernel.org> <david@redhat.com>
|
||||||
Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com>
|
Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com>
|
||||||
Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.com>
|
Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.com>
|
||||||
Frederic Konrad <konrad.frederic@yahoo.fr> <konrad@adacore.com>
|
Frederic Konrad <konrad.frederic@yahoo.fr> <konrad@adacore.com>
|
||||||
|
|
@ -136,6 +138,7 @@ Chen Gang <gang.chen.5i5j@gmail.com>
|
||||||
Chen Gang <gang.chen@sunrus.com.cn>
|
Chen Gang <gang.chen@sunrus.com.cn>
|
||||||
Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
|
Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
|
||||||
Christophe Lyon <christophe.lyon@st.com>
|
Christophe Lyon <christophe.lyon@st.com>
|
||||||
|
Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
|
||||||
Collin L. Walling <walling@linux.ibm.com>
|
Collin L. Walling <walling@linux.ibm.com>
|
||||||
Daniel P. Berrangé <berrange@redhat.com>
|
Daniel P. Berrangé <berrange@redhat.com>
|
||||||
Eduardo Otubo <otubo@redhat.com>
|
Eduardo Otubo <otubo@redhat.com>
|
||||||
|
|
|
||||||
358
MAINTAINERS
358
MAINTAINERS
File diff suppressed because it is too large
Load diff
4
Makefile
4
Makefile
|
|
@ -96,6 +96,8 @@ meson.stamp: config-host.mak
|
||||||
|
|
||||||
# 3. ensure meson-generated build files are up-to-date
|
# 3. ensure meson-generated build files are up-to-date
|
||||||
|
|
||||||
|
ninja-cmd-goals =
|
||||||
|
|
||||||
ifneq ($(NINJA),)
|
ifneq ($(NINJA),)
|
||||||
Makefile.ninja: build.ninja
|
Makefile.ninja: build.ninja
|
||||||
$(quiet-@){ \
|
$(quiet-@){ \
|
||||||
|
|
@ -150,7 +152,7 @@ NINJAFLAGS = \
|
||||||
$(or $(filter -l% -j%, $(MAKEFLAGS)), \
|
$(or $(filter -l% -j%, $(MAKEFLAGS)), \
|
||||||
$(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \
|
$(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \
|
||||||
-d keepdepfile
|
-d keepdepfile
|
||||||
ninja-cmd-goals = $(or $(MAKECMDGOALS), all)
|
ninja-cmd-goals += $(or $(MAKECMDGOALS), all)
|
||||||
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
|
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
|
||||||
|
|
||||||
makefile-targets := build.ninja ctags TAGS cscope dist clean
|
makefile-targets := build.ninja ctags TAGS cscope dist clean
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
10.1.0
|
10.2.1
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ config TCG
|
||||||
config KVM
|
config KVM
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config MSHV
|
||||||
|
bool
|
||||||
|
|
||||||
config XEN
|
config XEN
|
||||||
bool
|
bool
|
||||||
select FSDEV_9P if VIRTFS
|
select FSDEV_9P if VIRTFS
|
||||||
|
|
|
||||||
106
accel/accel-irq.c
Normal file
106
accel/accel-irq.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -43,6 +43,7 @@ static void *dummy_cpu_thread_fn(void *arg)
|
||||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
qemu_process_cpu_events(cpu);
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
do {
|
do {
|
||||||
|
|
@ -57,7 +58,6 @@ static void *dummy_cpu_thread_fn(void *arg)
|
||||||
qemu_sem_wait(&cpu->sem);
|
qemu_sem_wait(&cpu->sem);
|
||||||
#endif
|
#endif
|
||||||
bql_lock();
|
bql_lock();
|
||||||
qemu_wait_io_event(cpu);
|
|
||||||
} while (!cpu->unplug);
|
} while (!cpu->unplug);
|
||||||
|
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
|
||||||
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
|
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
|
||||||
{
|
{
|
||||||
if (!cpu->vcpu_dirty) {
|
if (!cpu->vcpu_dirty) {
|
||||||
hvf_get_registers(cpu);
|
hvf_arch_get_registers(cpu);
|
||||||
cpu->vcpu_dirty = true;
|
cpu->vcpu_dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,10 +148,11 @@ static int hvf_init_vcpu(CPUState *cpu)
|
||||||
sigact.sa_handler = dummy_signal;
|
sigact.sa_handler = dummy_signal;
|
||||||
sigaction(SIG_IPI, &sigact, NULL);
|
sigaction(SIG_IPI, &sigact, NULL);
|
||||||
|
|
||||||
|
#ifdef __aarch64__
|
||||||
pthread_sigmask(SIG_BLOCK, NULL, &cpu->accel->unblock_ipi_mask);
|
pthread_sigmask(SIG_BLOCK, NULL, &cpu->accel->unblock_ipi_mask);
|
||||||
sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI);
|
sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI);
|
||||||
|
cpu->accel->guest_debug_enabled = false;
|
||||||
|
|
||||||
#ifdef __aarch64__
|
|
||||||
r = hv_vcpu_create(&cpu->accel->fd,
|
r = hv_vcpu_create(&cpu->accel->fd,
|
||||||
(hv_vcpu_exit_t **)&cpu->accel->exit, NULL);
|
(hv_vcpu_exit_t **)&cpu->accel->exit, NULL);
|
||||||
#else
|
#else
|
||||||
|
|
@ -160,8 +161,6 @@ static int hvf_init_vcpu(CPUState *cpu)
|
||||||
assert_hvf_ok(r);
|
assert_hvf_ok(r);
|
||||||
cpu->vcpu_dirty = true;
|
cpu->vcpu_dirty = true;
|
||||||
|
|
||||||
cpu->accel->guest_debug_enabled = false;
|
|
||||||
|
|
||||||
return hvf_arch_init_vcpu(cpu);
|
return hvf_arch_init_vcpu(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,13 +191,13 @@ static void *hvf_cpu_thread_fn(void *arg)
|
||||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
qemu_process_cpu_events(cpu);
|
||||||
if (cpu_can_run(cpu)) {
|
if (cpu_can_run(cpu)) {
|
||||||
r = hvf_vcpu_exec(cpu);
|
r = hvf_arch_vcpu_exec(cpu);
|
||||||
if (r == EXCP_DEBUG) {
|
if (r == EXCP_DEBUG) {
|
||||||
cpu_handle_guest_debug(cpu);
|
cpu_handle_guest_debug(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu_wait_io_event(cpu);
|
|
||||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||||
|
|
||||||
hvf_vcpu_destroy(cpu);
|
hvf_vcpu_destroy(cpu);
|
||||||
|
|
|
||||||
|
|
@ -47,13 +47,14 @@ static void *kvm_vcpu_thread_fn(void *arg)
|
||||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
qemu_process_cpu_events(cpu);
|
||||||
|
|
||||||
if (cpu_can_run(cpu)) {
|
if (cpu_can_run(cpu)) {
|
||||||
r = kvm_cpu_exec(cpu);
|
r = kvm_cpu_exec(cpu);
|
||||||
if (r == EXCP_DEBUG) {
|
if (r == EXCP_DEBUG) {
|
||||||
cpu_handle_guest_debug(cpu);
|
cpu_handle_guest_debug(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu_wait_io_event(cpu);
|
|
||||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||||
|
|
||||||
kvm_destroy_vcpu(cpu);
|
kvm_destroy_vcpu(cpu);
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,13 @@
|
||||||
#include "system/runstate.h"
|
#include "system/runstate.h"
|
||||||
#include "system/cpus.h"
|
#include "system/cpus.h"
|
||||||
#include "system/accel-blocker.h"
|
#include "system/accel-blocker.h"
|
||||||
|
#include "system/physmem.h"
|
||||||
|
#include "system/ramblock.h"
|
||||||
#include "accel/accel-ops.h"
|
#include "accel/accel-ops.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "exec/tswap.h"
|
#include "exec/tswap.h"
|
||||||
|
#include "exec/target_page.h"
|
||||||
#include "system/memory.h"
|
#include "system/memory.h"
|
||||||
#include "system/ram_addr.h"
|
|
||||||
#include "qemu/event_notifier.h"
|
#include "qemu/event_notifier.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
@ -358,7 +360,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
||||||
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
|
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
|
||||||
{
|
{
|
||||||
KVMState *s = kvm_state;
|
KVMState *s = kvm_state;
|
||||||
struct kvm_userspace_memory_region2 mem;
|
struct kvm_userspace_memory_region2 mem = {};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mem.slot = slot->slot | (kml->as_id << 16);
|
mem.slot = slot->slot | (kml->as_id << 16);
|
||||||
|
|
@ -414,7 +416,7 @@ err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_park_vcpu(CPUState *cpu)
|
static void kvm_park_vcpu(CPUState *cpu)
|
||||||
{
|
{
|
||||||
struct KVMParkedVcpu *vcpu;
|
struct KVMParkedVcpu *vcpu;
|
||||||
|
|
||||||
|
|
@ -426,7 +428,7 @@ void kvm_park_vcpu(CPUState *cpu)
|
||||||
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
|
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
|
static int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
|
||||||
{
|
{
|
||||||
struct KVMParkedVcpu *cpu;
|
struct KVMParkedVcpu *cpu;
|
||||||
int kvm_fd = -ENOENT;
|
int kvm_fd = -ENOENT;
|
||||||
|
|
@ -523,7 +525,8 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If I am the CPU that created coalesced_mmio_ring, then discard it */
|
/* If I am the CPU that created coalesced_mmio_ring, then discard it */
|
||||||
if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) {
|
if (s->coalesced_mmio_ring ==
|
||||||
|
(void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE) {
|
||||||
s->coalesced_mmio_ring = NULL;
|
s->coalesced_mmio_ring = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -756,7 +759,7 @@ static void kvm_slot_sync_dirty_pages(KVMSlot *slot)
|
||||||
ram_addr_t start = slot->ram_start_offset;
|
ram_addr_t start = slot->ram_start_offset;
|
||||||
ram_addr_t pages = slot->memory_size / qemu_real_host_page_size();
|
ram_addr_t pages = slot->memory_size / qemu_real_host_page_size();
|
||||||
|
|
||||||
cpu_physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages);
|
physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_slot_reset_dirty_pages(KVMSlot *slot)
|
static void kvm_slot_reset_dirty_pages(KVMSlot *slot)
|
||||||
|
|
@ -1595,7 +1598,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||||
mem->ram = ram;
|
mem->ram = ram;
|
||||||
mem->flags = kvm_mem_flags(mr);
|
mem->flags = kvm_mem_flags(mr);
|
||||||
mem->guest_memfd = mr->ram_block->guest_memfd;
|
mem->guest_memfd = mr->ram_block->guest_memfd;
|
||||||
mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host;
|
mem->guest_memfd_offset = mem->guest_memfd >= 0 ?
|
||||||
|
(uint8_t*)ram - mr->ram_block->host : 0;
|
||||||
|
|
||||||
kvm_slot_init_dirty_bitmap(mem);
|
kvm_slot_init_dirty_bitmap(mem);
|
||||||
err = kvm_set_user_memory_region(kml, mem, true);
|
err = kvm_set_user_memory_region(kml, mem, true);
|
||||||
|
|
@ -2776,8 +2780,8 @@ static int kvm_init(AccelState *as, MachineState *ms)
|
||||||
|
|
||||||
kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES);
|
kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES);
|
||||||
kvm_guest_memfd_supported =
|
kvm_guest_memfd_supported =
|
||||||
kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
|
kvm_vm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
|
||||||
kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
|
kvm_vm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
|
||||||
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
|
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
|
||||||
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
|
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
|
||||||
|
|
||||||
|
|
@ -2934,22 +2938,32 @@ void kvm_cpu_synchronize_state(CPUState *cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
|
static bool kvm_cpu_synchronize_put(CPUState *cpu, KvmPutState state,
|
||||||
|
const char *desc)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err);
|
int ret = kvm_arch_put_registers(cpu, state, &err);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (err) {
|
if (err) {
|
||||||
error_reportf_err(err, "Restoring resisters after reset: ");
|
error_reportf_err(err, "Restoring resisters %s: ", desc);
|
||||||
} else {
|
} else {
|
||||||
error_report("Failed to put registers after reset: %s",
|
error_report("Failed to put registers %s: %s", desc,
|
||||||
strerror(-ret));
|
strerror(-ret));
|
||||||
}
|
}
|
||||||
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
|
return false;
|
||||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu->vcpu_dirty = false;
|
cpu->vcpu_dirty = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
|
||||||
|
{
|
||||||
|
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RESET_STATE, "after reset")) {
|
||||||
|
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
|
||||||
|
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
|
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
|
||||||
|
|
@ -2963,19 +2977,9 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu)
|
||||||
|
|
||||||
static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
|
static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_FULL_STATE, "after init")) {
|
||||||
int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err);
|
|
||||||
if (ret) {
|
|
||||||
if (err) {
|
|
||||||
error_reportf_err(err, "Putting registers after init: ");
|
|
||||||
} else {
|
|
||||||
error_report("Failed to put registers after init: %s",
|
|
||||||
strerror(-ret));
|
|
||||||
}
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu->vcpu_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_cpu_synchronize_post_init(CPUState *cpu)
|
void kvm_cpu_synchronize_post_init(CPUState *cpu)
|
||||||
|
|
@ -3029,10 +3033,6 @@ static void kvm_eat_signals(CPUState *cpu)
|
||||||
|
|
||||||
if (kvm_immediate_exit) {
|
if (kvm_immediate_exit) {
|
||||||
qatomic_set(&cpu->kvm_run->immediate_exit, 0);
|
qatomic_set(&cpu->kvm_run->immediate_exit, 0);
|
||||||
/* Write kvm_run->immediate_exit before the cpu->exit_request
|
|
||||||
* write in kvm_cpu_exec.
|
|
||||||
*/
|
|
||||||
smp_wmb();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3159,7 +3159,6 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||||
trace_kvm_cpu_exec();
|
trace_kvm_cpu_exec();
|
||||||
|
|
||||||
if (kvm_arch_process_async_events(cpu)) {
|
if (kvm_arch_process_async_events(cpu)) {
|
||||||
qatomic_set(&cpu->exit_request, 0);
|
|
||||||
return EXCP_HLT;
|
return EXCP_HLT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3170,24 +3169,16 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||||
MemTxAttrs attrs;
|
MemTxAttrs attrs;
|
||||||
|
|
||||||
if (cpu->vcpu_dirty) {
|
if (cpu->vcpu_dirty) {
|
||||||
Error *err = NULL;
|
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RUNTIME_STATE,
|
||||||
ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err);
|
"at runtime")) {
|
||||||
if (ret) {
|
|
||||||
if (err) {
|
|
||||||
error_reportf_err(err, "Putting registers after init: ");
|
|
||||||
} else {
|
|
||||||
error_report("Failed to put registers after init: %s",
|
|
||||||
strerror(-ret));
|
|
||||||
}
|
|
||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu->vcpu_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_arch_pre_run(cpu, run);
|
kvm_arch_pre_run(cpu, run);
|
||||||
if (qatomic_read(&cpu->exit_request)) {
|
/* Corresponding store-release is in cpu_exit. */
|
||||||
|
if (qatomic_load_acquire(&cpu->exit_request)) {
|
||||||
trace_kvm_interrupt_exit_request();
|
trace_kvm_interrupt_exit_request();
|
||||||
/*
|
/*
|
||||||
* KVM requires us to reenter the kernel after IO exits to complete
|
* KVM requires us to reenter the kernel after IO exits to complete
|
||||||
|
|
@ -3197,13 +3188,15 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||||
kvm_cpu_kick_self();
|
kvm_cpu_kick_self();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read cpu->exit_request before KVM_RUN reads run->immediate_exit.
|
|
||||||
* Matching barrier in kvm_eat_signals.
|
|
||||||
*/
|
|
||||||
smp_rmb();
|
|
||||||
|
|
||||||
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
|
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After writing cpu->exit_request, cpu_exit() sends a signal that writes
|
||||||
|
* kvm->run->immediate_exit. The signal is already happening after the
|
||||||
|
* write to cpu->exit_request so, if KVM read kvm->run->immediate_exit
|
||||||
|
* as true, cpu->exit_request will always read as true.
|
||||||
|
*/
|
||||||
|
|
||||||
attrs = kvm_arch_post_run(cpu, run);
|
attrs = kvm_arch_post_run(cpu, run);
|
||||||
|
|
||||||
#ifdef KVM_HAVE_MCE_INJECTION
|
#ifdef KVM_HAVE_MCE_INJECTION
|
||||||
|
|
@ -3346,7 +3339,6 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
qatomic_set(&cpu->exit_request, 0);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3381,10 +3373,10 @@ int kvm_vm_ioctl(KVMState *s, unsigned long type, ...)
|
||||||
trace_kvm_vm_ioctl(type, arg);
|
trace_kvm_vm_ioctl(type, arg);
|
||||||
accel_ioctl_begin();
|
accel_ioctl_begin();
|
||||||
ret = ioctl(s->vmfd, type, arg);
|
ret = ioctl(s->vmfd, type, arg);
|
||||||
accel_ioctl_end();
|
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
|
accel_ioctl_end();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3421,10 +3413,10 @@ int kvm_device_ioctl(int fd, unsigned long type, ...)
|
||||||
trace_kvm_device_ioctl(fd, type, arg);
|
trace_kvm_device_ioctl(fd, type, arg);
|
||||||
accel_ioctl_begin();
|
accel_ioctl_begin();
|
||||||
ret = ioctl(fd, type, arg);
|
ret = ioctl(fd, type, arg);
|
||||||
accel_ioctl_end();
|
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
|
accel_ioctl_end();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3731,7 +3723,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
|
||||||
have_sigbus_pending = true;
|
have_sigbus_pending = true;
|
||||||
pending_sigbus_addr = addr;
|
pending_sigbus_addr = addr;
|
||||||
pending_sigbus_code = code;
|
pending_sigbus_code = code;
|
||||||
qatomic_set(&cpu->exit_request, 1);
|
qatomic_set(&cpu->exit_request, true);
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
common_ss.add(files('accel-common.c'))
|
common_ss.add(files('accel-common.c'))
|
||||||
specific_ss.add(files('accel-target.c'))
|
specific_ss.add(files('accel-target.c'))
|
||||||
system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c'))
|
system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c', 'accel-irq.c'))
|
||||||
user_ss.add(files('accel-user.c'))
|
user_ss.add(files('accel-user.c'))
|
||||||
|
|
||||||
subdir('tcg')
|
subdir('tcg')
|
||||||
|
|
@ -10,6 +10,7 @@ if have_system
|
||||||
subdir('kvm')
|
subdir('kvm')
|
||||||
subdir('xen')
|
subdir('xen')
|
||||||
subdir('stubs')
|
subdir('stubs')
|
||||||
|
subdir('mshv')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# qtest
|
# qtest
|
||||||
|
|
|
||||||
399
accel/mshv/irq.c
Normal file
399
accel/mshv/irq.c
Normal 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
563
accel/mshv/mem.c
Normal 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, ®ion);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, ®ion);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
9
accel/mshv/meson.build
Normal 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
730
accel/mshv/mshv-all.c
Normal 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)∈
|
||||||
|
|
||||||
|
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)∈
|
||||||
|
|
||||||
|
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
375
accel/mshv/msr.c
Normal 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
33
accel/mshv/trace-events
Normal 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
14
accel/mshv/trace.h
Normal 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"
|
||||||
|
|
@ -5,5 +5,6 @@ system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
|
||||||
system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
|
system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
|
||||||
system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
|
system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
|
||||||
system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
|
system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
|
||||||
|
system_stubs_ss.add(when: 'CONFIG_MSHV', if_false: files('mshv-stub.c'))
|
||||||
|
|
||||||
specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss)
|
specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss)
|
||||||
|
|
|
||||||
44
accel/stubs/mshv-stub.c
Normal file
44
accel/stubs/mshv-stub.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -17,8 +17,3 @@ G_NORETURN void cpu_loop_exit(CPUState *cpu)
|
||||||
{
|
{
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
|
||||||
{
|
|
||||||
g_assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -122,5 +122,14 @@ GEN_ATOMIC_HELPERS(umax_fetch)
|
||||||
|
|
||||||
GEN_ATOMIC_HELPERS(xchg)
|
GEN_ATOMIC_HELPERS(xchg)
|
||||||
|
|
||||||
|
#if HAVE_CMPXCHG128
|
||||||
|
ATOMIC_HELPER(xchgo_be, Int128)
|
||||||
|
ATOMIC_HELPER(xchgo_le, Int128)
|
||||||
|
ATOMIC_HELPER(fetch_ando_be, Int128)
|
||||||
|
ATOMIC_HELPER(fetch_ando_le, Int128)
|
||||||
|
ATOMIC_HELPER(fetch_oro_be, Int128)
|
||||||
|
ATOMIC_HELPER(fetch_oro_le, Int128)
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef ATOMIC_HELPER
|
#undef ATOMIC_HELPER
|
||||||
#undef GEN_ATOMIC_HELPERS
|
#undef GEN_ATOMIC_HELPERS
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DATA_SIZE < 16
|
|
||||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
MemOpIdx oi, uintptr_t retaddr)
|
MemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +107,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
DATA_SIZE, retaddr);
|
DATA_SIZE, retaddr);
|
||||||
DATA_TYPE ret;
|
DATA_TYPE ret;
|
||||||
|
|
||||||
|
#if DATA_SIZE == 16
|
||||||
|
ret = atomic16_xchg(haddr, val);
|
||||||
|
#else
|
||||||
ret = qatomic_xchg__nocheck(haddr, val);
|
ret = qatomic_xchg__nocheck(haddr, val);
|
||||||
|
#endif
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
atomic_trace_rmw_post(env, addr,
|
atomic_trace_rmw_post(env, addr,
|
||||||
VALUE_LOW(ret),
|
VALUE_LOW(ret),
|
||||||
|
|
@ -119,6 +122,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DATA_SIZE == 16
|
||||||
|
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
|
MemOpIdx oi, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
|
||||||
|
DATA_SIZE, retaddr);
|
||||||
|
DATA_TYPE ret = atomic16_fetch_and(haddr, val);
|
||||||
|
ATOMIC_MMU_CLEANUP;
|
||||||
|
atomic_trace_rmw_post(env, addr,
|
||||||
|
VALUE_LOW(ret),
|
||||||
|
VALUE_HIGH(ret),
|
||||||
|
VALUE_LOW(val),
|
||||||
|
VALUE_HIGH(val),
|
||||||
|
oi);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
|
MemOpIdx oi, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
|
||||||
|
DATA_SIZE, retaddr);
|
||||||
|
DATA_TYPE ret = atomic16_fetch_or(haddr, val);
|
||||||
|
ATOMIC_MMU_CLEANUP;
|
||||||
|
atomic_trace_rmw_post(env, addr,
|
||||||
|
VALUE_LOW(ret),
|
||||||
|
VALUE_HIGH(ret),
|
||||||
|
VALUE_LOW(val),
|
||||||
|
VALUE_HIGH(val),
|
||||||
|
oi);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#else
|
||||||
#define GEN_ATOMIC_HELPER(X) \
|
#define GEN_ATOMIC_HELPER(X) \
|
||||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
||||||
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
||||||
|
|
@ -188,7 +224,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
|
||||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||||
|
|
||||||
#undef GEN_ATOMIC_HELPER_FN
|
#undef GEN_ATOMIC_HELPER_FN
|
||||||
#endif /* DATA SIZE < 16 */
|
#endif /* DATA SIZE == 16 */
|
||||||
|
|
||||||
#undef END
|
#undef END
|
||||||
|
|
||||||
|
|
@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
|
||||||
return BSWAP(ret);
|
return BSWAP(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DATA_SIZE < 16
|
|
||||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
MemOpIdx oi, uintptr_t retaddr)
|
MemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
|
|
@ -233,7 +268,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
DATA_SIZE, retaddr);
|
DATA_SIZE, retaddr);
|
||||||
ABI_TYPE ret;
|
ABI_TYPE ret;
|
||||||
|
|
||||||
|
#if DATA_SIZE == 16
|
||||||
|
ret = atomic16_xchg(haddr, BSWAP(val));
|
||||||
|
#else
|
||||||
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
|
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
|
||||||
|
#endif
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
atomic_trace_rmw_post(env, addr,
|
atomic_trace_rmw_post(env, addr,
|
||||||
VALUE_LOW(ret),
|
VALUE_LOW(ret),
|
||||||
|
|
@ -244,6 +283,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
return BSWAP(ret);
|
return BSWAP(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DATA_SIZE == 16
|
||||||
|
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
|
MemOpIdx oi, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
|
||||||
|
DATA_SIZE, retaddr);
|
||||||
|
DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val));
|
||||||
|
ATOMIC_MMU_CLEANUP;
|
||||||
|
atomic_trace_rmw_post(env, addr,
|
||||||
|
VALUE_LOW(ret),
|
||||||
|
VALUE_HIGH(ret),
|
||||||
|
VALUE_LOW(val),
|
||||||
|
VALUE_HIGH(val),
|
||||||
|
oi);
|
||||||
|
return BSWAP(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||||
|
MemOpIdx oi, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
|
||||||
|
DATA_SIZE, retaddr);
|
||||||
|
DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val));
|
||||||
|
ATOMIC_MMU_CLEANUP;
|
||||||
|
atomic_trace_rmw_post(env, addr,
|
||||||
|
VALUE_LOW(ret),
|
||||||
|
VALUE_HIGH(ret),
|
||||||
|
VALUE_LOW(val),
|
||||||
|
VALUE_HIGH(val),
|
||||||
|
oi);
|
||||||
|
return BSWAP(ret);
|
||||||
|
}
|
||||||
|
#else
|
||||||
#define GEN_ATOMIC_HELPER(X) \
|
#define GEN_ATOMIC_HELPER(X) \
|
||||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
||||||
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
||||||
|
|
@ -317,7 +389,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||||
#undef ADD
|
#undef ADD
|
||||||
|
|
||||||
#undef GEN_ATOMIC_HELPER_FN
|
#undef GEN_ATOMIC_HELPER_FN
|
||||||
#endif /* DATA_SIZE < 16 */
|
#endif /* DATA_SIZE == 16 */
|
||||||
|
|
||||||
#undef END
|
#undef END
|
||||||
#endif /* DATA_SIZE > 1 */
|
#endif /* DATA_SIZE > 1 */
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
#include "exec/replay-core.h"
|
#include "exec/replay-core.h"
|
||||||
#include "system/tcg.h"
|
#include "system/tcg.h"
|
||||||
#include "exec/helper-proto-common.h"
|
#include "exec/helper-proto-common.h"
|
||||||
|
#include "tcg-accel-ops.h"
|
||||||
#include "tb-jmp-cache.h"
|
#include "tb-jmp-cache.h"
|
||||||
#include "tb-hash.h"
|
#include "tb-hash.h"
|
||||||
#include "tb-context.h"
|
#include "tb-context.h"
|
||||||
|
|
@ -748,6 +749,20 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tcg_kick_vcpu_thread(CPUState *cpu)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Ensure cpu_exec will see the reason why the exit request was set.
|
||||||
|
* FIXME: this is not always needed. Other accelerators instead
|
||||||
|
* read interrupt_request and set exit_request on demand from the
|
||||||
|
* CPU thread; see kvm_arch_pre_run() for example.
|
||||||
|
*/
|
||||||
|
qatomic_store_release(&cpu->exit_request, true);
|
||||||
|
|
||||||
|
/* Ensure cpu_exec will see the exit request after TCG has exited. */
|
||||||
|
qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool icount_exit_request(CPUState *cpu)
|
static inline bool icount_exit_request(CPUState *cpu)
|
||||||
{
|
{
|
||||||
if (!icount_enabled()) {
|
if (!icount_enabled()) {
|
||||||
|
|
@ -774,44 +789,47 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||||
/* Clear the interrupt flag now since we're processing
|
/* Clear the interrupt flag now since we're processing
|
||||||
* cpu->interrupt_request and cpu->exit_request.
|
* cpu->interrupt_request and cpu->exit_request.
|
||||||
* Ensure zeroing happens before reading cpu->exit_request or
|
* Ensure zeroing happens before reading cpu->exit_request or
|
||||||
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
|
* cpu->interrupt_request (see also store-release in
|
||||||
|
* tcg_kick_vcpu_thread())
|
||||||
*/
|
*/
|
||||||
qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0);
|
qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0);
|
||||||
|
|
||||||
if (unlikely(qatomic_read(&cpu->interrupt_request))) {
|
#ifdef CONFIG_USER_ONLY
|
||||||
int interrupt_request;
|
assert(!cpu_test_interrupt(cpu, ~0));
|
||||||
|
#else
|
||||||
|
if (unlikely(cpu_test_interrupt(cpu, ~0))) {
|
||||||
bql_lock();
|
bql_lock();
|
||||||
interrupt_request = cpu->interrupt_request;
|
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) {
|
||||||
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
|
cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG);
|
||||||
/* Mask out external interrupts for this step. */
|
|
||||||
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
|
|
||||||
}
|
|
||||||
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
|
|
||||||
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
|
||||||
cpu->exception_index = EXCP_DEBUG;
|
cpu->exception_index = EXCP_DEBUG;
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
|
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
} else if (interrupt_request & CPU_INTERRUPT_HALT) {
|
} else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) {
|
||||||
replay_interrupt();
|
replay_interrupt();
|
||||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
|
||||||
cpu->halted = 1;
|
cpu->halted = 1;
|
||||||
cpu->exception_index = EXCP_HLT;
|
cpu->exception_index = EXCP_HLT;
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
|
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
|
||||||
|
int interrupt_request = cpu->interrupt_request;
|
||||||
|
|
||||||
if (interrupt_request & CPU_INTERRUPT_RESET) {
|
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) {
|
||||||
replay_interrupt();
|
replay_interrupt();
|
||||||
tcg_ops->cpu_exec_reset(cpu);
|
tcg_ops->cpu_exec_reset(cpu);
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
|
||||||
|
/* Mask out external interrupts for this step. */
|
||||||
|
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The target hook has 3 exit conditions:
|
* The target hook has 3 exit conditions:
|
||||||
* False when the interrupt isn't processed,
|
* False when the interrupt isn't processed,
|
||||||
|
|
@ -836,13 +854,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||||
cpu->exception_index = -1;
|
cpu->exception_index = -1;
|
||||||
*last_tb = NULL;
|
*last_tb = NULL;
|
||||||
}
|
}
|
||||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
|
||||||
* reload the 'interrupt_request' value */
|
|
||||||
interrupt_request = cpu->interrupt_request;
|
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_USER_ONLY */
|
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) {
|
||||||
if (interrupt_request & CPU_INTERRUPT_EXITTB) {
|
cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB);
|
||||||
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
|
|
||||||
/* ensure that no TB jump will be modified as
|
/* ensure that no TB jump will be modified as
|
||||||
the program flow was changed */
|
the program flow was changed */
|
||||||
*last_tb = NULL;
|
*last_tb = NULL;
|
||||||
|
|
@ -851,10 +865,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||||
/* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
|
/* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
}
|
}
|
||||||
|
#endif /* !CONFIG_USER_ONLY */
|
||||||
|
|
||||||
/* Finally, check if we need to exit to the main loop. */
|
/*
|
||||||
if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) {
|
* Finally, check if we need to exit to the main loop.
|
||||||
qatomic_set(&cpu->exit_request, 0);
|
* The corresponding store-release is in cpu_exit.
|
||||||
|
*/
|
||||||
|
if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) {
|
||||||
if (cpu->exception_index == -1) {
|
if (cpu->exception_index == -1) {
|
||||||
cpu->exception_index = EXCP_INTERRUPT;
|
cpu->exception_index = EXCP_INTERRUPT;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include "accel/tcg/probe.h"
|
#include "accel/tcg/probe.h"
|
||||||
#include "exec/page-protection.h"
|
#include "exec/page-protection.h"
|
||||||
#include "system/memory.h"
|
#include "system/memory.h"
|
||||||
|
#include "system/physmem.h"
|
||||||
#include "accel/tcg/cpu-ldst-common.h"
|
#include "accel/tcg/cpu-ldst-common.h"
|
||||||
#include "accel/tcg/cpu-mmu-index.h"
|
#include "accel/tcg/cpu-mmu-index.h"
|
||||||
#include "exec/cputlb.h"
|
#include "exec/cputlb.h"
|
||||||
|
|
@ -89,9 +90,6 @@
|
||||||
*/
|
*/
|
||||||
QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data));
|
QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data));
|
||||||
|
|
||||||
/* We currently can't handle more than 16 bits in the MMUIDX bitmask.
|
|
||||||
*/
|
|
||||||
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
|
|
||||||
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
||||||
|
|
||||||
static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
|
static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
|
||||||
|
|
@ -129,7 +127,7 @@ static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry)
|
||||||
static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
|
static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
|
||||||
vaddr addr)
|
vaddr addr)
|
||||||
{
|
{
|
||||||
uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS;
|
uintptr_t size_mask = cpu_tlb_fast(cpu, mmu_idx)->mask >> CPU_TLB_ENTRY_BITS;
|
||||||
|
|
||||||
return (addr >> TARGET_PAGE_BITS) & size_mask;
|
return (addr >> TARGET_PAGE_BITS) & size_mask;
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +136,7 @@ static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
|
||||||
static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx,
|
static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx,
|
||||||
vaddr addr)
|
vaddr addr)
|
||||||
{
|
{
|
||||||
return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)];
|
return &cpu_tlb_fast(cpu, mmu_idx)->table[tlb_index(cpu, mmu_idx, addr)];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
|
static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
|
||||||
|
|
@ -292,7 +290,7 @@ static void tlb_flush_one_mmuidx_locked(CPUState *cpu, int mmu_idx,
|
||||||
int64_t now)
|
int64_t now)
|
||||||
{
|
{
|
||||||
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
|
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
|
||||||
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx];
|
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
|
||||||
|
|
||||||
tlb_mmu_resize_locked(desc, fast, now);
|
tlb_mmu_resize_locked(desc, fast, now);
|
||||||
tlb_mmu_flush_locked(desc, fast);
|
tlb_mmu_flush_locked(desc, fast);
|
||||||
|
|
@ -331,7 +329,7 @@ void tlb_init(CPUState *cpu)
|
||||||
cpu->neg.tlb.c.dirty = 0;
|
cpu->neg.tlb.c.dirty = 0;
|
||||||
|
|
||||||
for (i = 0; i < NB_MMU_MODES; i++) {
|
for (i = 0; i < NB_MMU_MODES; i++) {
|
||||||
tlb_mmu_init(&cpu->neg.tlb.d[i], &cpu->neg.tlb.f[i], now);
|
tlb_mmu_init(&cpu->neg.tlb.d[i], cpu_tlb_fast(cpu, i), now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,7 +340,7 @@ void tlb_destroy(CPUState *cpu)
|
||||||
qemu_spin_destroy(&cpu->neg.tlb.c.lock);
|
qemu_spin_destroy(&cpu->neg.tlb.c.lock);
|
||||||
for (i = 0; i < NB_MMU_MODES; i++) {
|
for (i = 0; i < NB_MMU_MODES; i++) {
|
||||||
CPUTLBDesc *desc = &cpu->neg.tlb.d[i];
|
CPUTLBDesc *desc = &cpu->neg.tlb.d[i];
|
||||||
CPUTLBDescFast *fast = &cpu->neg.tlb.f[i];
|
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, i);
|
||||||
|
|
||||||
g_free(fast->table);
|
g_free(fast->table);
|
||||||
g_free(desc->fulltlb);
|
g_free(desc->fulltlb);
|
||||||
|
|
@ -370,8 +368,8 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn,
|
||||||
|
|
||||||
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||||
{
|
{
|
||||||
uint16_t asked = data.host_int;
|
MMUIdxMap asked = data.host_int;
|
||||||
uint16_t all_dirty, work, to_clean;
|
MMUIdxMap all_dirty, work, to_clean;
|
||||||
int64_t now = get_clock_realtime();
|
int64_t now = get_clock_realtime();
|
||||||
|
|
||||||
assert_cpu_is_self(cpu);
|
assert_cpu_is_self(cpu);
|
||||||
|
|
@ -408,7 +406,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
|
void tlb_flush_by_mmuidx(CPUState *cpu, MMUIdxMap idxmap)
|
||||||
{
|
{
|
||||||
tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap);
|
tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap);
|
||||||
|
|
||||||
|
|
@ -422,7 +420,7 @@ void tlb_flush(CPUState *cpu)
|
||||||
tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS);
|
tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap)
|
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, MMUIdxMap idxmap)
|
||||||
{
|
{
|
||||||
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
|
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
|
||||||
|
|
||||||
|
|
@ -531,7 +529,7 @@ static void tlb_flush_page_locked(CPUState *cpu, int midx, vaddr page)
|
||||||
*/
|
*/
|
||||||
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
|
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
|
||||||
vaddr addr,
|
vaddr addr,
|
||||||
uint16_t idxmap)
|
MMUIdxMap idxmap)
|
||||||
{
|
{
|
||||||
int mmu_idx;
|
int mmu_idx;
|
||||||
|
|
||||||
|
|
@ -570,14 +568,14 @@ static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu,
|
||||||
{
|
{
|
||||||
vaddr addr_and_idxmap = data.target_ptr;
|
vaddr addr_and_idxmap = data.target_ptr;
|
||||||
vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK;
|
vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK;
|
||||||
uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
|
MMUIdxMap idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
|
||||||
|
|
||||||
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
|
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vaddr addr;
|
vaddr addr;
|
||||||
uint16_t idxmap;
|
MMUIdxMap idxmap;
|
||||||
} TLBFlushPageByMMUIdxData;
|
} TLBFlushPageByMMUIdxData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -599,7 +597,7 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu,
|
||||||
g_free(d);
|
g_free(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap)
|
void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, MMUIdxMap idxmap)
|
||||||
{
|
{
|
||||||
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap);
|
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap);
|
||||||
|
|
||||||
|
|
@ -618,7 +616,7 @@ void tlb_flush_page(CPUState *cpu, vaddr addr)
|
||||||
|
|
||||||
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||||
vaddr addr,
|
vaddr addr,
|
||||||
uint16_t idxmap)
|
MMUIdxMap idxmap)
|
||||||
{
|
{
|
||||||
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
|
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
|
||||||
|
|
||||||
|
|
@ -667,7 +665,7 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
|
||||||
unsigned bits)
|
unsigned bits)
|
||||||
{
|
{
|
||||||
CPUTLBDesc *d = &cpu->neg.tlb.d[midx];
|
CPUTLBDesc *d = &cpu->neg.tlb.d[midx];
|
||||||
CPUTLBDescFast *f = &cpu->neg.tlb.f[midx];
|
CPUTLBDescFast *f = cpu_tlb_fast(cpu, midx);
|
||||||
vaddr mask = MAKE_64BIT_MASK(0, bits);
|
vaddr mask = MAKE_64BIT_MASK(0, bits);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -715,8 +713,8 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vaddr addr;
|
vaddr addr;
|
||||||
vaddr len;
|
vaddr len;
|
||||||
uint16_t idxmap;
|
MMUIdxMap idxmap;
|
||||||
uint16_t bits;
|
unsigned bits;
|
||||||
} TLBFlushRangeData;
|
} TLBFlushRangeData;
|
||||||
|
|
||||||
static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
|
static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
|
||||||
|
|
@ -766,7 +764,7 @@ static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
|
void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
|
||||||
vaddr len, uint16_t idxmap,
|
vaddr len, MMUIdxMap idxmap,
|
||||||
unsigned bits)
|
unsigned bits)
|
||||||
{
|
{
|
||||||
TLBFlushRangeData d;
|
TLBFlushRangeData d;
|
||||||
|
|
@ -797,7 +795,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
|
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
|
||||||
uint16_t idxmap, unsigned bits)
|
MMUIdxMap idxmap, unsigned bits)
|
||||||
{
|
{
|
||||||
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
|
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
|
||||||
}
|
}
|
||||||
|
|
@ -805,7 +803,7 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
|
||||||
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||||
vaddr addr,
|
vaddr addr,
|
||||||
vaddr len,
|
vaddr len,
|
||||||
uint16_t idxmap,
|
MMUIdxMap idxmap,
|
||||||
unsigned bits)
|
unsigned bits)
|
||||||
{
|
{
|
||||||
TLBFlushRangeData d, *p;
|
TLBFlushRangeData d, *p;
|
||||||
|
|
@ -847,7 +845,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||||
|
|
||||||
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||||
vaddr addr,
|
vaddr addr,
|
||||||
uint16_t idxmap,
|
MMUIdxMap idxmap,
|
||||||
unsigned bits)
|
unsigned bits)
|
||||||
{
|
{
|
||||||
tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE,
|
tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE,
|
||||||
|
|
@ -858,7 +856,7 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||||
can be detected */
|
can be detected */
|
||||||
void tlb_protect_code(ram_addr_t ram_addr)
|
void tlb_protect_code(ram_addr_t ram_addr)
|
||||||
{
|
{
|
||||||
cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
|
physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
|
||||||
TARGET_PAGE_SIZE,
|
TARGET_PAGE_SIZE,
|
||||||
DIRTY_MEMORY_CODE);
|
DIRTY_MEMORY_CODE);
|
||||||
}
|
}
|
||||||
|
|
@ -867,7 +865,7 @@ void tlb_protect_code(ram_addr_t ram_addr)
|
||||||
tested for self modifying code */
|
tested for self modifying code */
|
||||||
void tlb_unprotect_code(ram_addr_t ram_addr)
|
void tlb_unprotect_code(ram_addr_t ram_addr)
|
||||||
{
|
{
|
||||||
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
|
physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -923,7 +921,7 @@ void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length)
|
||||||
qemu_spin_lock(&cpu->neg.tlb.c.lock);
|
qemu_spin_lock(&cpu->neg.tlb.c.lock);
|
||||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||||
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
|
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
|
||||||
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx];
|
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
|
||||||
unsigned int n = tlb_n_entries(fast);
|
unsigned int n = tlb_n_entries(fast);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
|
@ -1085,7 +1083,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
|
||||||
if (prot & PAGE_WRITE) {
|
if (prot & PAGE_WRITE) {
|
||||||
if (section->readonly) {
|
if (section->readonly) {
|
||||||
write_flags |= TLB_DISCARD_WRITE;
|
write_flags |= TLB_DISCARD_WRITE;
|
||||||
} else if (cpu_physical_memory_is_clean(iotlb)) {
|
} else if (physical_memory_is_clean(iotlb)) {
|
||||||
write_flags |= TLB_NOTDIRTY;
|
write_flags |= TLB_NOTDIRTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1316,7 +1314,7 @@ static bool victim_tlb_hit(CPUState *cpu, size_t mmu_idx, size_t index,
|
||||||
|
|
||||||
if (cmp == page) {
|
if (cmp == page) {
|
||||||
/* Found entry in victim tlb, swap tlb and iotlb. */
|
/* Found entry in victim tlb, swap tlb and iotlb. */
|
||||||
CPUTLBEntry tmptlb, *tlb = &cpu->neg.tlb.f[mmu_idx].table[index];
|
CPUTLBEntry tmptlb, *tlb = &cpu_tlb_fast(cpu, mmu_idx)->table[index];
|
||||||
|
|
||||||
qemu_spin_lock(&cpu->neg.tlb.c.lock);
|
qemu_spin_lock(&cpu->neg.tlb.c.lock);
|
||||||
copy_tlb_helper_locked(&tmptlb, tlb);
|
copy_tlb_helper_locked(&tmptlb, tlb);
|
||||||
|
|
@ -1341,7 +1339,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
|
||||||
|
|
||||||
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
|
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
|
||||||
|
|
||||||
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
|
if (!physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
|
||||||
tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr);
|
tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1349,10 +1347,10 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
|
||||||
* Set both VGA and migration bits for simplicity and to remove
|
* Set both VGA and migration bits for simplicity and to remove
|
||||||
* the notdirty callback faster.
|
* the notdirty callback faster.
|
||||||
*/
|
*/
|
||||||
cpu_physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE);
|
physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE);
|
||||||
|
|
||||||
/* We remove the notdirty callback only if the code has been flushed. */
|
/* We remove the notdirty callback only if the code has been flushed. */
|
||||||
if (!cpu_physical_memory_is_clean(ram_addr)) {
|
if (!physical_memory_is_clean(ram_addr)) {
|
||||||
trace_memory_notdirty_set_dirty(mem_vaddr);
|
trace_memory_notdirty_set_dirty(mem_vaddr);
|
||||||
tlb_set_dirty(cpu, mem_vaddr);
|
tlb_set_dirty(cpu, mem_vaddr);
|
||||||
}
|
}
|
||||||
|
|
@ -1670,18 +1668,7 @@ static bool mmu_lookup1(CPUState *cpu, MMULookupPageData *data, MemOp memop,
|
||||||
|
|
||||||
if (likely(!maybe_resized)) {
|
if (likely(!maybe_resized)) {
|
||||||
/* Alignment has not been checked by tlb_fill_align. */
|
/* Alignment has not been checked by tlb_fill_align. */
|
||||||
int a_bits = memop_alignment_bits(memop);
|
int a_bits = memop_tlb_alignment_bits(memop, flags & TLB_CHECK_ALIGNED);
|
||||||
|
|
||||||
/*
|
|
||||||
* This alignment check differs from the one above, in that this is
|
|
||||||
* based on the atomicity of the operation. The intended use case is
|
|
||||||
* the ARM memory type field of each PTE, where access to pages with
|
|
||||||
* Device memory type require alignment.
|
|
||||||
*/
|
|
||||||
if (unlikely(flags & TLB_CHECK_ALIGNED)) {
|
|
||||||
int at_bits = memop_atomicity_bits(memop);
|
|
||||||
a_bits = MAX(a_bits, at_bits);
|
|
||||||
}
|
|
||||||
if (unlikely(addr & ((1 << a_bits) - 1))) {
|
if (unlikely(addr & ((1 << a_bits) - 1))) {
|
||||||
cpu_unaligned_access(cpu, addr, access_type, mmu_idx, ra);
|
cpu_unaligned_access(cpu, addr, access_type, mmu_idx, ra);
|
||||||
}
|
}
|
||||||
|
|
@ -1744,6 +1731,7 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
||||||
uintptr_t ra, MMUAccessType type, MMULookupLocals *l)
|
uintptr_t ra, MMUAccessType type, MMULookupLocals *l)
|
||||||
{
|
{
|
||||||
bool crosspage;
|
bool crosspage;
|
||||||
|
vaddr last;
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
l->memop = get_memop(oi);
|
l->memop = get_memop(oi);
|
||||||
|
|
@ -1753,13 +1741,15 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
||||||
|
|
||||||
l->page[0].addr = addr;
|
l->page[0].addr = addr;
|
||||||
l->page[0].size = memop_size(l->memop);
|
l->page[0].size = memop_size(l->memop);
|
||||||
l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK;
|
l->page[1].addr = 0;
|
||||||
l->page[1].size = 0;
|
l->page[1].size = 0;
|
||||||
crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK;
|
|
||||||
|
|
||||||
if (likely(!crosspage)) {
|
/* Lookup and recognize exceptions from the first page. */
|
||||||
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
|
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
|
||||||
|
|
||||||
|
last = addr + l->page[0].size - 1;
|
||||||
|
crosspage = (addr ^ last) & TARGET_PAGE_MASK;
|
||||||
|
if (likely(!crosspage)) {
|
||||||
flags = l->page[0].flags;
|
flags = l->page[0].flags;
|
||||||
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
|
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
|
||||||
mmu_watch_or_dirty(cpu, &l->page[0], type, ra);
|
mmu_watch_or_dirty(cpu, &l->page[0], type, ra);
|
||||||
|
|
@ -1769,18 +1759,18 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Finish compute of page crossing. */
|
/* Finish compute of page crossing. */
|
||||||
int size0 = l->page[1].addr - addr;
|
vaddr addr1 = last & TARGET_PAGE_MASK;
|
||||||
|
int size0 = addr1 - addr;
|
||||||
l->page[1].size = l->page[0].size - size0;
|
l->page[1].size = l->page[0].size - size0;
|
||||||
l->page[0].size = size0;
|
l->page[0].size = size0;
|
||||||
|
|
||||||
l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx,
|
l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx,
|
||||||
l->page[1].addr, addr);
|
addr1, addr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lookup both pages, recognizing exceptions from either. If the
|
* Lookup and recognize exceptions from the second page.
|
||||||
* second lookup potentially resized, refresh first CPUTLBEntryFull.
|
* If the lookup potentially resized the table, refresh the
|
||||||
|
* first CPUTLBEntryFull pointer.
|
||||||
*/
|
*/
|
||||||
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
|
|
||||||
if (mmu_lookup1(cpu, &l->page[1], 0, l->mmu_idx, type, ra)) {
|
if (mmu_lookup1(cpu, &l->page[1], 0, l->mmu_idx, type, ra)) {
|
||||||
uintptr_t index = tlb_index(cpu, l->mmu_idx, addr);
|
uintptr_t index = tlb_index(cpu, l->mmu_idx, addr);
|
||||||
l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index];
|
l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index];
|
||||||
|
|
|
||||||
|
|
@ -102,8 +102,8 @@ static TCGv_i32 gen_cpu_index(void)
|
||||||
/*
|
/*
|
||||||
* Optimize when we run with a single vcpu. All values using cpu_index,
|
* Optimize when we run with a single vcpu. All values using cpu_index,
|
||||||
* including scoreboard index, will be optimized out.
|
* including scoreboard index, will be optimized out.
|
||||||
* User-mode calls tb_flush when setting this flag. In system-mode, all
|
* User-mode flushes all TBs when setting this flag.
|
||||||
* vcpus are created before generating code.
|
* In system-mode, all vcpus are created before generating code.
|
||||||
*/
|
*/
|
||||||
if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) {
|
if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) {
|
||||||
return tcg_constant_i32(current_cpu->cpu_index);
|
return tcg_constant_i32(current_cpu->cpu_index);
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,11 @@
|
||||||
#include "internal-common.h"
|
#include "internal-common.h"
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
#include "user/page-protection.h"
|
#include "user/page-protection.h"
|
||||||
|
#define runstate_is_running() true
|
||||||
|
#else
|
||||||
|
#include "system/runstate.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
/* List iterators for lists of tagged pointers in TranslationBlock. */
|
/* List iterators for lists of tagged pointers in TranslationBlock. */
|
||||||
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \
|
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \
|
||||||
|
|
@ -88,7 +91,10 @@ static IntervalTreeRoot tb_root;
|
||||||
|
|
||||||
static void tb_remove_all(void)
|
static void tb_remove_all(void)
|
||||||
{
|
{
|
||||||
assert_memory_lock();
|
/*
|
||||||
|
* Only called from tb_flush__exclusive_or_serial, where we have already
|
||||||
|
* asserted that we're in an exclusive state.
|
||||||
|
*/
|
||||||
memset(&tb_root, 0, sizeof(tb_root));
|
memset(&tb_root, 0, sizeof(tb_root));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -756,17 +762,20 @@ static void tb_remove(TranslationBlock *tb)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_USER_ONLY */
|
#endif /* CONFIG_USER_ONLY */
|
||||||
|
|
||||||
/* flush all the translation blocks */
|
/*
|
||||||
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
* Flush all the translation blocks.
|
||||||
|
* Must be called from a context in which no cpus are running,
|
||||||
|
* e.g. start_exclusive() or vm_stop().
|
||||||
|
*/
|
||||||
|
void tb_flush__exclusive_or_serial(void)
|
||||||
{
|
{
|
||||||
bool did_flush = false;
|
CPUState *cpu;
|
||||||
|
|
||||||
mmap_lock();
|
trace_tb_flush();
|
||||||
/* If it is already been done on request of another CPU, just retry. */
|
assert(tcg_enabled());
|
||||||
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
|
/* Note that cpu_in_serial_context checks cpu_in_exclusive_context. */
|
||||||
goto done;
|
assert(!runstate_is_running() ||
|
||||||
}
|
(current_cpu && cpu_in_serial_context(current_cpu)));
|
||||||
did_flush = true;
|
|
||||||
|
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
tcg_flush_jmp_cache(cpu);
|
tcg_flush_jmp_cache(cpu);
|
||||||
|
|
@ -778,27 +787,25 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||||
tcg_region_reset_all();
|
tcg_region_reset_all();
|
||||||
/* XXX: flush processor icache at this point if cache flush is expensive */
|
/* XXX: flush processor icache at this point if cache flush is expensive */
|
||||||
qatomic_inc(&tb_ctx.tb_flush_count);
|
qatomic_inc(&tb_ctx.tb_flush_count);
|
||||||
|
|
||||||
done:
|
|
||||||
mmap_unlock();
|
|
||||||
if (did_flush) {
|
|
||||||
qemu_plugin_flush_cb();
|
qemu_plugin_flush_cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||||
|
{
|
||||||
|
/* If it is already been done on request of another CPU, just retry. */
|
||||||
|
if (tb_ctx.tb_flush_count == tb_flush_count.host_int) {
|
||||||
|
tb_flush__exclusive_or_serial();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tb_flush(CPUState *cpu)
|
void queue_tb_flush(CPUState *cs)
|
||||||
{
|
{
|
||||||
if (tcg_enabled()) {
|
if (tcg_enabled()) {
|
||||||
unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count);
|
unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count);
|
||||||
|
async_safe_run_on_cpu(cs, do_tb_flush,
|
||||||
if (cpu_in_serial_context(cpu)) {
|
|
||||||
do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
|
|
||||||
} else {
|
|
||||||
async_safe_run_on_cpu(cpu, do_tb_flush,
|
|
||||||
RUN_ON_CPU_HOST_INT(tb_flush_count));
|
RUN_ON_CPU_HOST_INT(tb_flush_count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* remove @orig from its @n_orig-th jump list */
|
/* remove @orig from its @n_orig-th jump list */
|
||||||
static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
|
static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
|
||||||
|
|
@ -836,6 +843,14 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
|
||||||
* We first acquired the lock, and since the destination pointer matches,
|
* We first acquired the lock, and since the destination pointer matches,
|
||||||
* we know for sure that @orig is in the jmp list.
|
* we know for sure that @orig is in the jmp list.
|
||||||
*/
|
*/
|
||||||
|
if (dest == orig) {
|
||||||
|
/*
|
||||||
|
* In the case of a TB that links to itself, removing the entry
|
||||||
|
* from the list means that it won't be present later during
|
||||||
|
* tb_jmp_unlink -- unlink now.
|
||||||
|
*/
|
||||||
|
tb_reset_jump(orig, n_orig);
|
||||||
|
}
|
||||||
pprev = &dest->jmp_list_head;
|
pprev = &dest->jmp_list_head;
|
||||||
TB_FOR_EACH_JMP(dest, tb, n) {
|
TB_FOR_EACH_JMP(dest, tb, n) {
|
||||||
if (tb == orig && n == n_orig) {
|
if (tb == orig && n == n_orig) {
|
||||||
|
|
@ -1154,7 +1169,6 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
|
||||||
page_collection_unlock(pages);
|
page_collection_unlock(pages);
|
||||||
/* Force execution of one insn next time. */
|
/* Force execution of one insn next time. */
|
||||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
|
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
|
||||||
mmap_unlock();
|
|
||||||
cpu_loop_exit_noexc(cpu);
|
cpu_loop_exit_noexc(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,9 @@ static void *mttcg_cpu_thread_fn(void *arg)
|
||||||
cpu_thread_signal_created(cpu);
|
cpu_thread_signal_created(cpu);
|
||||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||||
|
|
||||||
/* process any pending work */
|
|
||||||
cpu->exit_request = 1;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
qemu_process_cpu_events(cpu);
|
||||||
|
|
||||||
if (cpu_can_run(cpu)) {
|
if (cpu_can_run(cpu)) {
|
||||||
int r;
|
int r;
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
|
|
@ -112,8 +111,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_wait_io_event(cpu);
|
|
||||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||||
|
|
||||||
tcg_cpu_destroy(cpu);
|
tcg_cpu_destroy(cpu);
|
||||||
|
|
@ -123,11 +120,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mttcg_kick_vcpu_thread(CPUState *cpu)
|
|
||||||
{
|
|
||||||
cpu_exit(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mttcg_start_vcpu_thread(CPUState *cpu)
|
void mttcg_start_vcpu_thread(CPUState *cpu)
|
||||||
{
|
{
|
||||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@
|
||||||
#ifndef TCG_ACCEL_OPS_MTTCG_H
|
#ifndef TCG_ACCEL_OPS_MTTCG_H
|
||||||
#define TCG_ACCEL_OPS_MTTCG_H
|
#define TCG_ACCEL_OPS_MTTCG_H
|
||||||
|
|
||||||
/* kick MTTCG vCPU thread */
|
|
||||||
void mttcg_kick_vcpu_thread(CPUState *cpu);
|
|
||||||
|
|
||||||
/* start an mttcg vCPU thread */
|
/* start an mttcg vCPU thread */
|
||||||
void mttcg_start_vcpu_thread(CPUState *cpu);
|
void mttcg_start_vcpu_thread(CPUState *cpu);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused)
|
||||||
CPUState *cpu;
|
CPUState *cpu;
|
||||||
|
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
cpu_exit(cpu);
|
tcg_kick_vcpu_thread(cpu);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ static void rr_wait_io_event(void)
|
||||||
rr_start_kick_timer();
|
rr_start_kick_timer();
|
||||||
|
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
qemu_wait_io_event_common(cpu);
|
qemu_process_cpu_events_common(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,13 +197,13 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||||
|
|
||||||
/* wait for initial kick-off after machine start */
|
/* wait for initial kick-off after machine start */
|
||||||
while (first_cpu->stopped) {
|
while (cpu_is_stopped(first_cpu)) {
|
||||||
qemu_cond_wait_bql(first_cpu->halt_cond);
|
qemu_cond_wait_bql(first_cpu->halt_cond);
|
||||||
|
|
||||||
/* process any pending work */
|
/* process any pending work */
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
current_cpu = cpu;
|
current_cpu = cpu;
|
||||||
qemu_wait_io_event_common(cpu);
|
qemu_process_cpu_events_common(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,13 +211,30 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||||
|
|
||||||
cpu = first_cpu;
|
cpu = first_cpu;
|
||||||
|
|
||||||
/* process any pending work */
|
|
||||||
cpu->exit_request = 1;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
/* Only used for icount_enabled() */
|
/* Only used for icount_enabled() */
|
||||||
int64_t cpu_budget = 0;
|
int64_t cpu_budget = 0;
|
||||||
|
|
||||||
|
if (cpu) {
|
||||||
|
/*
|
||||||
|
* This could even reset exit_request for all CPUs, but in practice
|
||||||
|
* races between CPU exits and changes to "cpu" are so rare that
|
||||||
|
* there's no advantage in doing so.
|
||||||
|
*/
|
||||||
|
qatomic_set(&cpu->exit_request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icount_enabled() && all_cpu_threads_idle()) {
|
||||||
|
/*
|
||||||
|
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
|
||||||
|
* in the main_loop, wake it up in order to start the warp timer.
|
||||||
|
*/
|
||||||
|
qemu_notify_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
rr_wait_io_event();
|
||||||
|
rr_deal_with_unplugged_cpus();
|
||||||
|
|
||||||
bql_unlock();
|
bql_unlock();
|
||||||
replay_mutex_lock();
|
replay_mutex_lock();
|
||||||
bql_lock();
|
bql_lock();
|
||||||
|
|
@ -242,10 +259,17 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||||
cpu = first_cpu;
|
cpu = first_cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
|
while (cpu && cpu_work_list_empty(cpu)) {
|
||||||
/* Store rr_current_cpu before evaluating cpu_can_run(). */
|
/*
|
||||||
|
* Store rr_current_cpu before evaluating cpu->exit_request.
|
||||||
|
* Pairs with rr_kick_next_cpu().
|
||||||
|
*/
|
||||||
qatomic_set_mb(&rr_current_cpu, cpu);
|
qatomic_set_mb(&rr_current_cpu, cpu);
|
||||||
|
|
||||||
|
/* Pairs with store-release in cpu_exit. */
|
||||||
|
if (qatomic_load_acquire(&cpu->exit_request)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
current_cpu = cpu;
|
current_cpu = cpu;
|
||||||
|
|
||||||
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
|
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
|
||||||
|
|
@ -285,21 +309,6 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||||
|
|
||||||
/* Does not need a memory barrier because a spurious wakeup is okay. */
|
/* Does not need a memory barrier because a spurious wakeup is okay. */
|
||||||
qatomic_set(&rr_current_cpu, NULL);
|
qatomic_set(&rr_current_cpu, NULL);
|
||||||
|
|
||||||
if (cpu && cpu->exit_request) {
|
|
||||||
qatomic_set_mb(&cpu->exit_request, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icount_enabled() && all_cpu_threads_idle()) {
|
|
||||||
/*
|
|
||||||
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
|
|
||||||
* in the main_loop, wake it up in order to start the warp timer.
|
|
||||||
*/
|
|
||||||
qemu_notify_event();
|
|
||||||
}
|
|
||||||
|
|
||||||
rr_wait_io_event();
|
|
||||||
rr_deal_with_unplugged_cpus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,6 @@ int tcg_cpu_exec(CPUState *cpu)
|
||||||
ret = cpu_exec(cpu);
|
ret = cpu_exec(cpu);
|
||||||
cpu_exec_end(cpu);
|
cpu_exec_end(cpu);
|
||||||
|
|
||||||
qatomic_set_mb(&cpu->exit_request, 0);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +95,7 @@ static void tcg_cpu_reset_hold(CPUState *cpu)
|
||||||
/* mask must never be zero, except for A20 change call */
|
/* mask must never be zero, except for A20 change call */
|
||||||
void tcg_handle_interrupt(CPUState *cpu, int mask)
|
void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||||
{
|
{
|
||||||
cpu->interrupt_request |= mask;
|
cpu_set_interrupt(cpu, mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If called from iothread context, wake the target cpu in
|
* If called from iothread context, wake the target cpu in
|
||||||
|
|
@ -206,7 +204,7 @@ static void tcg_accel_ops_init(AccelClass *ac)
|
||||||
|
|
||||||
if (qemu_tcg_mttcg_enabled()) {
|
if (qemu_tcg_mttcg_enabled()) {
|
||||||
ops->create_vcpu_thread = mttcg_start_vcpu_thread;
|
ops->create_vcpu_thread = mttcg_start_vcpu_thread;
|
||||||
ops->kick_vcpu_thread = mttcg_kick_vcpu_thread;
|
ops->kick_vcpu_thread = tcg_kick_vcpu_thread;
|
||||||
ops->handle_interrupt = tcg_handle_interrupt;
|
ops->handle_interrupt = tcg_handle_interrupt;
|
||||||
} else {
|
} else {
|
||||||
ops->create_vcpu_thread = rr_start_vcpu_thread;
|
ops->create_vcpu_thread = rr_start_vcpu_thread;
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,6 @@ void tcg_cpu_destroy(CPUState *cpu);
|
||||||
int tcg_cpu_exec(CPUState *cpu);
|
int tcg_cpu_exec(CPUState *cpu);
|
||||||
void tcg_handle_interrupt(CPUState *cpu, int mask);
|
void tcg_handle_interrupt(CPUState *cpu, int mask);
|
||||||
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
|
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
|
||||||
|
void tcg_kick_vcpu_thread(CPUState *cpu);
|
||||||
|
|
||||||
#endif /* TCG_ACCEL_OPS_H */
|
#endif /* TCG_ACCEL_OPS_H */
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@
|
||||||
#include "qemu/target-info.h"
|
#include "qemu/target-info.h"
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
|
#include "exec/tb-flush.h"
|
||||||
|
#include "system/runstate.h"
|
||||||
#endif
|
#endif
|
||||||
#include "accel/accel-ops.h"
|
#include "accel/accel-ops.h"
|
||||||
#include "accel/accel-cpu-ops.h"
|
#include "accel/accel-cpu-ops.h"
|
||||||
|
|
@ -82,6 +84,23 @@ static void tcg_accel_instance_init(Object *obj)
|
||||||
|
|
||||||
bool one_insn_per_tb;
|
bool one_insn_per_tb;
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void tcg_vm_change_state(void *opaque, bool running, RunState state)
|
||||||
|
{
|
||||||
|
if (state == RUN_STATE_RESTORE_VM) {
|
||||||
|
/*
|
||||||
|
* loadvm will update the content of RAM, bypassing the usual
|
||||||
|
* mechanisms that ensure we flush TBs for writes to memory
|
||||||
|
* we've translated code from, so we must flush all TBs.
|
||||||
|
*
|
||||||
|
* vm_stop() has just stopped all cpus, so we are exclusive.
|
||||||
|
*/
|
||||||
|
assert(!running);
|
||||||
|
tb_flush__exclusive_or_serial();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int tcg_init_machine(AccelState *as, MachineState *ms)
|
static int tcg_init_machine(AccelState *as, MachineState *ms)
|
||||||
{
|
{
|
||||||
TCGState *s = TCG_STATE(as);
|
TCGState *s = TCG_STATE(as);
|
||||||
|
|
@ -124,6 +143,8 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_add_vm_change_state_handler(tcg_vm_change_state, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tcg_allowed = true;
|
tcg_allowed = true;
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,18 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG,
|
||||||
i128, env, i64, i128, i128, i32)
|
i128, env, i64, i128, i128, i32)
|
||||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
|
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
|
||||||
i128, env, i64, i128, i128, i32)
|
i128, env, i64, i128, i128, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG,
|
||||||
|
i128, env, i64, i128, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG,
|
||||||
|
i128, env, i64, i128, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG,
|
||||||
|
i128, env, i64, i128, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG,
|
||||||
|
i128, env, i64, i128, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG,
|
||||||
|
i128, env, i64, i128, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG,
|
||||||
|
i128, env, i64, i128, i32)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG,
|
DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64
|
||||||
|
|
||||||
# translate-all.c
|
# translate-all.c
|
||||||
translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
||||||
|
tb_gen_code_buffer_overflow(const char *reason) "reason: %s"
|
||||||
|
|
||||||
# ldst_atomicity
|
# ldst_atomicity
|
||||||
load_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
load_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
||||||
|
|
@ -24,3 +25,6 @@ store_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIx
|
||||||
store_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
store_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
||||||
store_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
store_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
||||||
store_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
store_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
|
||||||
|
|
||||||
|
# tb-maint.c
|
||||||
|
tb_flush(void) ""
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
|
||||||
tb = tcg_tb_alloc(tcg_ctx);
|
tb = tcg_tb_alloc(tcg_ctx);
|
||||||
if (unlikely(!tb)) {
|
if (unlikely(!tb)) {
|
||||||
/* flush must be done */
|
/* flush must be done */
|
||||||
tb_flush(cpu);
|
if (cpu_in_serial_context(cpu)) {
|
||||||
|
trace_tb_gen_code_buffer_overflow("tcg_tb_alloc");
|
||||||
|
tb_flush__exclusive_or_serial();
|
||||||
|
goto buffer_overflow;
|
||||||
|
}
|
||||||
|
queue_tb_flush(cpu);
|
||||||
mmap_unlock();
|
mmap_unlock();
|
||||||
/* Make the execution loop process the flush as soon as possible. */
|
/* Make the execution loop process the flush as soon as possible. */
|
||||||
cpu->exception_index = EXCP_INTERRUPT;
|
cpu->exception_index = EXCP_INTERRUPT;
|
||||||
|
|
@ -321,6 +326,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
|
||||||
if (unlikely(gen_code_size < 0)) {
|
if (unlikely(gen_code_size < 0)) {
|
||||||
switch (gen_code_size) {
|
switch (gen_code_size) {
|
||||||
case -1:
|
case -1:
|
||||||
|
trace_tb_gen_code_buffer_overflow("setjmp_gen_code");
|
||||||
/*
|
/*
|
||||||
* Overflow of code_gen_buffer, or the current slice of it.
|
* Overflow of code_gen_buffer, or the current slice of it.
|
||||||
*
|
*
|
||||||
|
|
@ -385,6 +391,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
|
||||||
|
|
||||||
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
|
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
|
||||||
if (unlikely(search_size < 0)) {
|
if (unlikely(search_size < 0)) {
|
||||||
|
trace_tb_gen_code_buffer_overflow("encode_search");
|
||||||
tb_unlock_pages(tb);
|
tb_unlock_pages(tb);
|
||||||
goto buffer_overflow;
|
goto buffer_overflow;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include "qemu/int128.h"
|
#include "qemu/int128.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "tcg/tcg-ldst.h"
|
#include "tcg/tcg-ldst.h"
|
||||||
|
#include "tcg-accel-ops.h"
|
||||||
#include "backend-ldst.h"
|
#include "backend-ldst.h"
|
||||||
#include "internal-common.h"
|
#include "internal-common.h"
|
||||||
#include "tb-internal.h"
|
#include "tb-internal.h"
|
||||||
|
|
@ -46,11 +47,15 @@ __thread uintptr_t helper_retaddr;
|
||||||
|
|
||||||
//#define DEBUG_SIGNAL
|
//#define DEBUG_SIGNAL
|
||||||
|
|
||||||
void cpu_interrupt(CPUState *cpu, int mask)
|
void qemu_cpu_kick(CPUState *cpu)
|
||||||
{
|
{
|
||||||
g_assert(bql_locked());
|
tcg_kick_vcpu_thread(cpu);
|
||||||
cpu->interrupt_request |= mask;
|
}
|
||||||
qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
|
|
||||||
|
void qemu_process_cpu_events(CPUState *cpu)
|
||||||
|
{
|
||||||
|
qatomic_set(&cpu->exit_request, false);
|
||||||
|
process_queued_cpu_work(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -264,48 +269,6 @@ static void pageflags_create(vaddr start, vaddr last, int flags)
|
||||||
interval_tree_insert(&p->itree, &pageflags_root);
|
interval_tree_insert(&p->itree, &pageflags_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A subroutine of page_set_flags: remove everything in [start,last]. */
|
|
||||||
static bool pageflags_unset(vaddr start, vaddr last)
|
|
||||||
{
|
|
||||||
bool inval_tb = false;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
PageFlagsNode *p = pageflags_find(start, last);
|
|
||||||
vaddr p_last;
|
|
||||||
|
|
||||||
if (!p) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->flags & PAGE_EXEC) {
|
|
||||||
inval_tb = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
interval_tree_remove(&p->itree, &pageflags_root);
|
|
||||||
p_last = p->itree.last;
|
|
||||||
|
|
||||||
if (p->itree.start < start) {
|
|
||||||
/* Truncate the node from the end, or split out the middle. */
|
|
||||||
p->itree.last = start - 1;
|
|
||||||
interval_tree_insert(&p->itree, &pageflags_root);
|
|
||||||
if (last < p_last) {
|
|
||||||
pageflags_create(last + 1, p_last, p->flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (p_last <= last) {
|
|
||||||
/* Range completely covers node -- remove it. */
|
|
||||||
g_free_rcu(p, rcu);
|
|
||||||
} else {
|
|
||||||
/* Truncate the node from the start. */
|
|
||||||
p->itree.start = last + 1;
|
|
||||||
interval_tree_insert(&p->itree, &pageflags_root);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inval_tb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A subroutine of page_set_flags: nothing overlaps [start,last],
|
* A subroutine of page_set_flags: nothing overlaps [start,last],
|
||||||
* but check adjacent mappings and maybe merge into a single range.
|
* but check adjacent mappings and maybe merge into a single range.
|
||||||
|
|
@ -351,15 +314,6 @@ static void pageflags_create_merge(vaddr start, vaddr last, int flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Allow the target to decide if PAGE_TARGET_[12] may be reset.
|
|
||||||
* By default, they are not kept.
|
|
||||||
*/
|
|
||||||
#ifndef PAGE_TARGET_STICKY
|
|
||||||
#define PAGE_TARGET_STICKY 0
|
|
||||||
#endif
|
|
||||||
#define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY)
|
|
||||||
|
|
||||||
/* A subroutine of page_set_flags: add flags to [start,last]. */
|
/* A subroutine of page_set_flags: add flags to [start,last]. */
|
||||||
static bool pageflags_set_clear(vaddr start, vaddr last,
|
static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
int set_flags, int clear_flags)
|
int set_flags, int clear_flags)
|
||||||
|
|
@ -372,7 +326,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
restart:
|
restart:
|
||||||
p = pageflags_find(start, last);
|
p = pageflags_find(start, last);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
if (set_flags) {
|
if (set_flags & PAGE_VALID) {
|
||||||
pageflags_create_merge(start, last, set_flags);
|
pageflags_create_merge(start, last, set_flags);
|
||||||
}
|
}
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -386,11 +340,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Need to flush if an overlapping executable region
|
* Need to flush if an overlapping executable region
|
||||||
* removes exec, or adds write.
|
* removes exec, adds write, or is a new mapping.
|
||||||
*/
|
*/
|
||||||
if ((p_flags & PAGE_EXEC)
|
if ((p_flags & PAGE_EXEC)
|
||||||
&& (!(merge_flags & PAGE_EXEC)
|
&& (!(merge_flags & PAGE_EXEC)
|
||||||
|| (merge_flags & ~p_flags & PAGE_WRITE))) {
|
|| (merge_flags & ~p_flags & PAGE_WRITE)
|
||||||
|
|| (clear_flags & PAGE_VALID))) {
|
||||||
inval_tb = true;
|
inval_tb = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,7 +354,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
* attempting to merge with adjacent regions.
|
* attempting to merge with adjacent regions.
|
||||||
*/
|
*/
|
||||||
if (start == p_start && last == p_last) {
|
if (start == p_start && last == p_last) {
|
||||||
if (merge_flags) {
|
if (merge_flags & PAGE_VALID) {
|
||||||
p->flags = merge_flags;
|
p->flags = merge_flags;
|
||||||
} else {
|
} else {
|
||||||
interval_tree_remove(&p->itree, &pageflags_root);
|
interval_tree_remove(&p->itree, &pageflags_root);
|
||||||
|
|
@ -419,12 +374,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
interval_tree_insert(&p->itree, &pageflags_root);
|
interval_tree_insert(&p->itree, &pageflags_root);
|
||||||
|
|
||||||
if (last < p_last) {
|
if (last < p_last) {
|
||||||
if (merge_flags) {
|
if (merge_flags & PAGE_VALID) {
|
||||||
pageflags_create(start, last, merge_flags);
|
pageflags_create(start, last, merge_flags);
|
||||||
}
|
}
|
||||||
pageflags_create(last + 1, p_last, p_flags);
|
pageflags_create(last + 1, p_last, p_flags);
|
||||||
} else {
|
} else {
|
||||||
if (merge_flags) {
|
if (merge_flags & PAGE_VALID) {
|
||||||
pageflags_create(start, p_last, merge_flags);
|
pageflags_create(start, p_last, merge_flags);
|
||||||
}
|
}
|
||||||
if (p_last < last) {
|
if (p_last < last) {
|
||||||
|
|
@ -433,18 +388,18 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (start < p_start && set_flags) {
|
if (start < p_start && (set_flags & PAGE_VALID)) {
|
||||||
pageflags_create(start, p_start - 1, set_flags);
|
pageflags_create(start, p_start - 1, set_flags);
|
||||||
}
|
}
|
||||||
if (last < p_last) {
|
if (last < p_last) {
|
||||||
interval_tree_remove(&p->itree, &pageflags_root);
|
interval_tree_remove(&p->itree, &pageflags_root);
|
||||||
p->itree.start = last + 1;
|
p->itree.start = last + 1;
|
||||||
interval_tree_insert(&p->itree, &pageflags_root);
|
interval_tree_insert(&p->itree, &pageflags_root);
|
||||||
if (merge_flags) {
|
if (merge_flags & PAGE_VALID) {
|
||||||
pageflags_create(start, last, merge_flags);
|
pageflags_create(start, last, merge_flags);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (merge_flags) {
|
if (merge_flags & PAGE_VALID) {
|
||||||
p->flags = merge_flags;
|
p->flags = merge_flags;
|
||||||
} else {
|
} else {
|
||||||
interval_tree_remove(&p->itree, &pageflags_root);
|
interval_tree_remove(&p->itree, &pageflags_root);
|
||||||
|
|
@ -492,7 +447,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
g_free_rcu(p, rcu);
|
g_free_rcu(p, rcu);
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
if (set_flags) {
|
if (set_flags & PAGE_VALID) {
|
||||||
pageflags_create(start, last, set_flags);
|
pageflags_create(start, last, set_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,42 +455,36 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||||
return inval_tb;
|
return inval_tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void page_set_flags(vaddr start, vaddr last, int flags)
|
void page_set_flags(vaddr start, vaddr last, int set_flags, int clear_flags)
|
||||||
{
|
{
|
||||||
bool reset = false;
|
/*
|
||||||
bool inval_tb = false;
|
* This function should never be called with addresses outside the
|
||||||
|
* guest address space. If this assert fires, it probably indicates
|
||||||
/* This function should never be called with addresses outside the
|
* a missing call to h2g_valid.
|
||||||
guest address space. If this assert fires, it probably indicates
|
*/
|
||||||
a missing call to h2g_valid. */
|
|
||||||
assert(start <= last);
|
assert(start <= last);
|
||||||
assert(last <= guest_addr_max);
|
assert(last <= guest_addr_max);
|
||||||
/* Only set PAGE_ANON with new mappings. */
|
|
||||||
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
|
|
||||||
assert_memory_lock();
|
assert_memory_lock();
|
||||||
|
|
||||||
start &= TARGET_PAGE_MASK;
|
start &= TARGET_PAGE_MASK;
|
||||||
last |= ~TARGET_PAGE_MASK;
|
last |= ~TARGET_PAGE_MASK;
|
||||||
|
|
||||||
if (!(flags & PAGE_VALID)) {
|
if (set_flags & PAGE_WRITE) {
|
||||||
flags = 0;
|
set_flags |= PAGE_WRITE_ORG;
|
||||||
} else {
|
|
||||||
reset = flags & PAGE_RESET;
|
|
||||||
flags &= ~PAGE_RESET;
|
|
||||||
if (flags & PAGE_WRITE) {
|
|
||||||
flags |= PAGE_WRITE_ORG;
|
|
||||||
}
|
}
|
||||||
|
if (clear_flags & PAGE_WRITE) {
|
||||||
|
clear_flags |= PAGE_WRITE_ORG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!flags || reset) {
|
if (clear_flags & PAGE_VALID) {
|
||||||
page_reset_target_data(start, last);
|
page_reset_target_data(start, last);
|
||||||
inval_tb |= pageflags_unset(start, last);
|
clear_flags = -1;
|
||||||
|
} else {
|
||||||
|
/* Only set PAGE_ANON with new mappings. */
|
||||||
|
assert(!(set_flags & PAGE_ANON));
|
||||||
}
|
}
|
||||||
if (flags) {
|
|
||||||
inval_tb |= pageflags_set_clear(start, last, flags,
|
if (pageflags_set_clear(start, last, set_flags, clear_flags)) {
|
||||||
~(reset ? 0 : PAGE_STICKY));
|
|
||||||
}
|
|
||||||
if (inval_tb) {
|
|
||||||
tb_invalidate_phys_range(NULL, start, last);
|
tb_invalidate_phys_range(NULL, start, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#pragma GCC diagnostic ignored "-Waddress"
|
#pragma GCC diagnostic ignored "-Waddress"
|
||||||
|
|
@ -41,7 +41,7 @@ struct pollhlp {
|
||||||
struct pollfd *pfds;
|
struct pollfd *pfds;
|
||||||
int count;
|
int count;
|
||||||
int mask;
|
int mask;
|
||||||
AudioState *s;
|
AudioBackend *s;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct ALSAVoiceOut {
|
typedef struct ALSAVoiceOut {
|
||||||
|
|
@ -264,7 +264,7 @@ static int alsa_poll_in (HWVoiceIn *hw)
|
||||||
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
|
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
static snd_pcm_format_t aud_to_alsafmt(AudioFormat fmt, bool big_endian)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case AUDIO_FORMAT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
|
|
@ -274,39 +274,19 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||||
return SND_PCM_FORMAT_U8;
|
return SND_PCM_FORMAT_U8;
|
||||||
|
|
||||||
case AUDIO_FORMAT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
if (endianness) {
|
return big_endian ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE;
|
||||||
return SND_PCM_FORMAT_S16_BE;
|
|
||||||
} else {
|
|
||||||
return SND_PCM_FORMAT_S16_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AUDIO_FORMAT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
if (endianness) {
|
return big_endian ? SND_PCM_FORMAT_U16_BE : SND_PCM_FORMAT_U16_LE;
|
||||||
return SND_PCM_FORMAT_U16_BE;
|
|
||||||
} else {
|
|
||||||
return SND_PCM_FORMAT_U16_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AUDIO_FORMAT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
if (endianness) {
|
return big_endian ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE;
|
||||||
return SND_PCM_FORMAT_S32_BE;
|
|
||||||
} else {
|
|
||||||
return SND_PCM_FORMAT_S32_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AUDIO_FORMAT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
if (endianness) {
|
return big_endian ? SND_PCM_FORMAT_U32_BE : SND_PCM_FORMAT_U32_LE;
|
||||||
return SND_PCM_FORMAT_U32_BE;
|
|
||||||
} else {
|
|
||||||
return SND_PCM_FORMAT_U32_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AUDIO_FORMAT_F32:
|
case AUDIO_FORMAT_F32:
|
||||||
if (endianness) {
|
return big_endian ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE;
|
||||||
return SND_PCM_FORMAT_FLOAT_BE;
|
|
||||||
} else {
|
|
||||||
return SND_PCM_FORMAT_FLOAT_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||||
|
|
@ -956,7 +936,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver alsa_audio_driver = {
|
static struct audio_driver alsa_audio_driver = {
|
||||||
.name = "alsa",
|
.name = "alsa",
|
||||||
.descr = "ALSA http://www.alsa-project.org",
|
|
||||||
.init = alsa_audio_init,
|
.init = alsa_audio_init,
|
||||||
.fini = alsa_audio_fini,
|
.fini = alsa_audio_fini,
|
||||||
.pcm_ops = &alsa_pcm_ops,
|
.pcm_ops = &alsa_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "audio/audio.h"
|
#include "audio_int.h"
|
||||||
#include "monitor/hmp.h"
|
#include "monitor/hmp.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qobject/qdict.h"
|
#include "qobject/qdict.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
|
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
|
||||||
|
|
||||||
|
|
@ -36,6 +37,8 @@ void hmp_info_capture(Monitor *mon, const QDict *qdict)
|
||||||
int i;
|
int i;
|
||||||
CaptureState *s;
|
CaptureState *s;
|
||||||
|
|
||||||
|
warn_report_once("'info capture' is deprecated since v10.2, to be removed");
|
||||||
|
|
||||||
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
||||||
monitor_printf(mon, "[%d]: ", i);
|
monitor_printf(mon, "[%d]: ", i);
|
||||||
s->ops.info (s->opaque);
|
s->ops.info (s->opaque);
|
||||||
|
|
@ -48,6 +51,8 @@ void hmp_stopcapture(Monitor *mon, const QDict *qdict)
|
||||||
int n = qdict_get_int(qdict, "n");
|
int n = qdict_get_int(qdict, "n");
|
||||||
CaptureState *s;
|
CaptureState *s;
|
||||||
|
|
||||||
|
warn_report_once("'stopcapture' is deprecated since v10.2, to be removed");
|
||||||
|
|
||||||
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
||||||
if (i == n) {
|
if (i == n) {
|
||||||
s->ops.destroy (s->opaque);
|
s->ops.destroy (s->opaque);
|
||||||
|
|
@ -67,7 +72,9 @@ void hmp_wavcapture(Monitor *mon, const QDict *qdict)
|
||||||
const char *audiodev = qdict_get_str(qdict, "audiodev");
|
const char *audiodev = qdict_get_str(qdict, "audiodev");
|
||||||
CaptureState *s;
|
CaptureState *s;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
AudioState *as = audio_state_by_name(audiodev, &local_err);
|
AudioBackend *as = audio_be_by_name(audiodev, &local_err);
|
||||||
|
|
||||||
|
warn_report_once("'wavcapture' is deprecated since v10.2, to be removed");
|
||||||
|
|
||||||
if (!as) {
|
if (!as) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
|
|
|
||||||
289
audio/audio.c
289
audio/audio.c
|
|
@ -23,9 +23,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
#include "monitor/monitor.h"
|
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/clone-visitor.h"
|
#include "qapi/clone-visitor.h"
|
||||||
|
|
@ -33,7 +32,6 @@
|
||||||
#include "qapi/qapi-visit-audio.h"
|
#include "qapi/qapi-visit-audio.h"
|
||||||
#include "qapi/qapi-commands-audio.h"
|
#include "qapi/qapi-commands-audio.h"
|
||||||
#include "qobject/qdict.h"
|
#include "qobject/qdict.h"
|
||||||
#include "qemu/cutils.h"
|
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
|
|
@ -41,7 +39,6 @@
|
||||||
#include "system/system.h"
|
#include "system/system.h"
|
||||||
#include "system/replay.h"
|
#include "system/replay.h"
|
||||||
#include "system/runstate.h"
|
#include "system/runstate.h"
|
||||||
#include "ui/qemu-spice.h"
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "audio"
|
#define AUDIO_CAP "audio"
|
||||||
|
|
@ -102,9 +99,7 @@ static audio_driver *audio_driver_lookup(const char *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
|
static AudioBackend *default_audio_be;
|
||||||
QTAILQ_HEAD_INITIALIZER(audio_states);
|
|
||||||
static AudioState *default_audio_state;
|
|
||||||
|
|
||||||
const struct mixeng_volume nominal_volume = {
|
const struct mixeng_volume nominal_volume = {
|
||||||
.mute = 0,
|
.mute = 0,
|
||||||
|
|
@ -279,7 +274,7 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
|
||||||
&& info->is_signed == is_signed
|
&& info->is_signed == is_signed
|
||||||
&& info->is_float == is_float
|
&& info->is_float == is_float
|
||||||
&& info->bits == bits
|
&& info->bits == bits
|
||||||
&& info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
|
&& info->swap_endianness == (as->endianness != HOST_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
|
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
|
||||||
|
|
@ -325,7 +320,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
|
||||||
info->nchannels = as->nchannels;
|
info->nchannels = as->nchannels;
|
||||||
info->bytes_per_frame = as->nchannels * mul;
|
info->bytes_per_frame = as->nchannels * mul;
|
||||||
info->bytes_per_second = info->freq * info->bytes_per_frame;
|
info->bytes_per_second = info->freq * info->bytes_per_frame;
|
||||||
info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
|
info->swap_endianness = (as->endianness != HOST_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
|
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
|
||||||
|
|
@ -385,7 +380,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
|
||||||
/*
|
/*
|
||||||
* Capture
|
* Capture
|
||||||
*/
|
*/
|
||||||
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
|
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioBackend *s,
|
||||||
struct audsettings *as)
|
struct audsettings *as)
|
||||||
{
|
{
|
||||||
CaptureVoiceOut *cap;
|
CaptureVoiceOut *cap;
|
||||||
|
|
@ -410,7 +405,7 @@ static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
|
static void audio_capture_maybe_changed(CaptureVoiceOut *cap, bool enabled)
|
||||||
{
|
{
|
||||||
if (cap->hw.enabled != enabled) {
|
if (cap->hw.enabled != enabled) {
|
||||||
audcnotification_e cmd;
|
audcnotification_e cmd;
|
||||||
|
|
@ -424,11 +419,11 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
|
||||||
{
|
{
|
||||||
HWVoiceOut *hw = &cap->hw;
|
HWVoiceOut *hw = &cap->hw;
|
||||||
SWVoiceOut *sw;
|
SWVoiceOut *sw;
|
||||||
int enabled = 0;
|
bool enabled = false;
|
||||||
|
|
||||||
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
|
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
|
||||||
if (sw->active) {
|
if (sw->active) {
|
||||||
enabled = 1;
|
enabled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -465,7 +460,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
|
||||||
|
|
||||||
static int audio_attach_capture (HWVoiceOut *hw)
|
static int audio_attach_capture (HWVoiceOut *hw)
|
||||||
{
|
{
|
||||||
AudioState *s = hw->s;
|
AudioBackend *s = hw->s;
|
||||||
CaptureVoiceOut *cap;
|
CaptureVoiceOut *cap;
|
||||||
|
|
||||||
audio_detach_capture (hw);
|
audio_detach_capture (hw);
|
||||||
|
|
@ -480,7 +475,7 @@ static int audio_attach_capture (HWVoiceOut *hw)
|
||||||
sw = &sc->sw;
|
sw = &sc->sw;
|
||||||
sw->hw = hw_cap;
|
sw->hw = hw_cap;
|
||||||
sw->info = hw->info;
|
sw->info = hw->info;
|
||||||
sw->empty = 1;
|
sw->empty = true;
|
||||||
sw->active = hw->enabled;
|
sw->active = hw->enabled;
|
||||||
sw->vol = nominal_volume;
|
sw->vol = nominal_volume;
|
||||||
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
|
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
|
||||||
|
|
@ -803,7 +798,7 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
|
||||||
/*
|
/*
|
||||||
* Timer
|
* Timer
|
||||||
*/
|
*/
|
||||||
static int audio_is_timer_needed(AudioState *s)
|
static int audio_is_timer_needed(AudioBackend *s)
|
||||||
{
|
{
|
||||||
HWVoiceIn *hwi = NULL;
|
HWVoiceIn *hwi = NULL;
|
||||||
HWVoiceOut *hwo = NULL;
|
HWVoiceOut *hwo = NULL;
|
||||||
|
|
@ -821,7 +816,7 @@ static int audio_is_timer_needed(AudioState *s)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_reset_timer (AudioState *s)
|
static void audio_reset_timer(AudioBackend *s)
|
||||||
{
|
{
|
||||||
if (audio_is_timer_needed(s)) {
|
if (audio_is_timer_needed(s)) {
|
||||||
timer_mod_anticipate_ns(s->ts,
|
timer_mod_anticipate_ns(s->ts,
|
||||||
|
|
@ -843,7 +838,7 @@ static void audio_reset_timer (AudioState *s)
|
||||||
static void audio_timer (void *opaque)
|
static void audio_timer (void *opaque)
|
||||||
{
|
{
|
||||||
int64_t now, diff;
|
int64_t now, diff;
|
||||||
AudioState *s = opaque;
|
AudioBackend *s = opaque;
|
||||||
|
|
||||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
diff = now - s->timer_last;
|
diff = now - s->timer_last;
|
||||||
|
|
@ -916,7 +911,7 @@ int AUD_get_buffer_size_out(SWVoiceOut *sw)
|
||||||
return sw->hw->samples * sw->hw->info.bytes_per_frame;
|
return sw->hw->samples * sw->hw->info.bytes_per_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUD_set_active_out (SWVoiceOut *sw, int on)
|
void AUD_set_active_out(SWVoiceOut *sw, bool on)
|
||||||
{
|
{
|
||||||
HWVoiceOut *hw;
|
HWVoiceOut *hw;
|
||||||
|
|
||||||
|
|
@ -926,14 +921,14 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||||
|
|
||||||
hw = sw->hw;
|
hw = sw->hw;
|
||||||
if (sw->active != on) {
|
if (sw->active != on) {
|
||||||
AudioState *s = sw->s;
|
AudioBackend *s = sw->s;
|
||||||
SWVoiceOut *temp_sw;
|
SWVoiceOut *temp_sw;
|
||||||
SWVoiceCap *sc;
|
SWVoiceCap *sc;
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
hw->pending_disable = 0;
|
hw->pending_disable = 0;
|
||||||
if (!hw->enabled) {
|
if (!hw->enabled) {
|
||||||
hw->enabled = 1;
|
hw->enabled = true;
|
||||||
if (s->vm_running) {
|
if (s->vm_running) {
|
||||||
if (hw->pcm_ops->enable_out) {
|
if (hw->pcm_ops->enable_out) {
|
||||||
hw->pcm_ops->enable_out(hw, true);
|
hw->pcm_ops->enable_out(hw, true);
|
||||||
|
|
@ -964,7 +959,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUD_set_active_in (SWVoiceIn *sw, int on)
|
void AUD_set_active_in(SWVoiceIn *sw, bool on)
|
||||||
{
|
{
|
||||||
HWVoiceIn *hw;
|
HWVoiceIn *hw;
|
||||||
|
|
||||||
|
|
@ -974,12 +969,12 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||||
|
|
||||||
hw = sw->hw;
|
hw = sw->hw;
|
||||||
if (sw->active != on) {
|
if (sw->active != on) {
|
||||||
AudioState *s = sw->s;
|
AudioBackend *s = sw->s;
|
||||||
SWVoiceIn *temp_sw;
|
SWVoiceIn *temp_sw;
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
if (!hw->enabled) {
|
if (!hw->enabled) {
|
||||||
hw->enabled = 1;
|
hw->enabled = true;
|
||||||
if (s->vm_running) {
|
if (s->vm_running) {
|
||||||
if (hw->pcm_ops->enable_in) {
|
if (hw->pcm_ops->enable_in) {
|
||||||
hw->pcm_ops->enable_in(hw, true);
|
hw->pcm_ops->enable_in(hw, true);
|
||||||
|
|
@ -998,7 +993,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nb_active == 1) {
|
if (nb_active == 1) {
|
||||||
hw->enabled = 0;
|
hw->enabled = false;
|
||||||
if (hw->pcm_ops->enable_in) {
|
if (hw->pcm_ops->enable_in) {
|
||||||
hw->pcm_ops->enable_in(hw, false);
|
hw->pcm_ops->enable_in(hw, false);
|
||||||
}
|
}
|
||||||
|
|
@ -1142,7 +1137,7 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
||||||
return clipped;
|
return clipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_run_out (AudioState *s)
|
static void audio_run_out(AudioBackend *s)
|
||||||
{
|
{
|
||||||
HWVoiceOut *hw = NULL;
|
HWVoiceOut *hw = NULL;
|
||||||
SWVoiceOut *sw;
|
SWVoiceOut *sw;
|
||||||
|
|
@ -1157,8 +1152,8 @@ static void audio_run_out (AudioState *s)
|
||||||
sw = hw->sw_head.lh_first;
|
sw = hw->sw_head.lh_first;
|
||||||
|
|
||||||
if (hw->pending_disable) {
|
if (hw->pending_disable) {
|
||||||
hw->enabled = 0;
|
hw->enabled = false;
|
||||||
hw->pending_disable = 0;
|
hw->pending_disable = false;
|
||||||
if (hw->pcm_ops->enable_out) {
|
if (hw->pcm_ops->enable_out) {
|
||||||
hw->pcm_ops->enable_out(hw, false);
|
hw->pcm_ops->enable_out(hw, false);
|
||||||
}
|
}
|
||||||
|
|
@ -1211,13 +1206,13 @@ static void audio_run_out (AudioState *s)
|
||||||
#ifdef DEBUG_OUT
|
#ifdef DEBUG_OUT
|
||||||
dolog ("Disabling voice\n");
|
dolog ("Disabling voice\n");
|
||||||
#endif
|
#endif
|
||||||
hw->enabled = 0;
|
hw->enabled = false;
|
||||||
hw->pending_disable = 0;
|
hw->pending_disable = false;
|
||||||
if (hw->pcm_ops->enable_out) {
|
if (hw->pcm_ops->enable_out) {
|
||||||
hw->pcm_ops->enable_out(hw, false);
|
hw->pcm_ops->enable_out(hw, false);
|
||||||
}
|
}
|
||||||
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
|
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
|
||||||
sc->sw.active = 0;
|
sc->sw.active = false;
|
||||||
audio_recalc_and_notify_capture (sc->cap);
|
audio_recalc_and_notify_capture (sc->cap);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1262,7 +1257,7 @@ static void audio_run_out (AudioState *s)
|
||||||
sw->total_hw_samples_mixed -= played;
|
sw->total_hw_samples_mixed -= played;
|
||||||
|
|
||||||
if (!sw->total_hw_samples_mixed) {
|
if (!sw->total_hw_samples_mixed) {
|
||||||
sw->empty = 1;
|
sw->empty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1296,7 +1291,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_run_in (AudioState *s)
|
static void audio_run_in(AudioBackend *s)
|
||||||
{
|
{
|
||||||
HWVoiceIn *hw = NULL;
|
HWVoiceIn *hw = NULL;
|
||||||
|
|
||||||
|
|
@ -1344,7 +1339,7 @@ static void audio_run_in (AudioState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_run_capture (AudioState *s)
|
static void audio_run_capture(AudioBackend *s)
|
||||||
{
|
{
|
||||||
CaptureVoiceOut *cap;
|
CaptureVoiceOut *cap;
|
||||||
|
|
||||||
|
|
@ -1391,7 +1386,7 @@ static void audio_run_capture (AudioState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_run(AudioState *s, const char *msg)
|
void audio_run(AudioBackend *s, const char *msg)
|
||||||
{
|
{
|
||||||
audio_run_out(s);
|
audio_run_out(s);
|
||||||
audio_run_in(s);
|
audio_run_in(s);
|
||||||
|
|
@ -1564,14 +1559,14 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
|
static bool audio_driver_init(AudioBackend *s, struct audio_driver *drv,
|
||||||
Audiodev *dev, Error **errp)
|
Audiodev *dev, Error **errp)
|
||||||
{
|
{
|
||||||
Error *local_err = NULL;
|
s->drv_opaque = drv->init(dev, errp);
|
||||||
|
if (!s->drv_opaque) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
s->drv_opaque = drv->init(dev, &local_err);
|
|
||||||
|
|
||||||
if (s->drv_opaque) {
|
|
||||||
if (!drv->pcm_ops->get_buffer_in) {
|
if (!drv->pcm_ops->get_buffer_in) {
|
||||||
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
|
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
|
||||||
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
|
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
|
||||||
|
|
@ -1584,21 +1579,20 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv,
|
||||||
audio_init_nb_voices_out(s, drv, 1);
|
audio_init_nb_voices_out(s, drv, 1);
|
||||||
audio_init_nb_voices_in(s, drv, 0);
|
audio_init_nb_voices_in(s, drv, 0);
|
||||||
s->drv = drv;
|
s->drv = drv;
|
||||||
return 0;
|
|
||||||
|
if (dev->timer_period <= 0) {
|
||||||
|
s->period_ticks = 1;
|
||||||
} else {
|
} else {
|
||||||
if (local_err) {
|
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
|
||||||
error_propagate(errp, local_err);
|
|
||||||
} else {
|
|
||||||
error_setg(errp, "Could not init `%s' audio driver", drv->name);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_vm_change_state_handler (void *opaque, bool running,
|
static void audio_vm_change_state_handler (void *opaque, bool running,
|
||||||
RunState state)
|
RunState state)
|
||||||
{
|
{
|
||||||
AudioState *s = opaque;
|
AudioBackend *s = opaque;
|
||||||
HWVoiceOut *hwo = NULL;
|
HWVoiceOut *hwo = NULL;
|
||||||
HWVoiceIn *hwi = NULL;
|
HWVoiceIn *hwi = NULL;
|
||||||
|
|
||||||
|
|
@ -1617,8 +1611,26 @@ static void audio_vm_change_state_handler (void *opaque, bool running,
|
||||||
audio_reset_timer (s);
|
audio_reset_timer (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_audio_state(AudioState *s)
|
static const VMStateDescription vmstate_audio;
|
||||||
|
|
||||||
|
static void audio_be_init(Object *obj)
|
||||||
{
|
{
|
||||||
|
AudioBackend *s = AUDIO_BACKEND(obj);
|
||||||
|
|
||||||
|
QLIST_INIT(&s->hw_head_out);
|
||||||
|
QLIST_INIT(&s->hw_head_in);
|
||||||
|
QLIST_INIT(&s->cap_head);
|
||||||
|
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
|
||||||
|
|
||||||
|
s->vmse = qemu_add_vm_change_state_handler(audio_vm_change_state_handler, s);
|
||||||
|
assert(s->vmse != NULL);
|
||||||
|
|
||||||
|
vmstate_register_any(NULL, &vmstate_audio, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audio_be_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
AudioBackend *s = AUDIO_BACKEND(obj);
|
||||||
HWVoiceOut *hwo, *hwon;
|
HWVoiceOut *hwo, *hwon;
|
||||||
HWVoiceIn *hwi, *hwin;
|
HWVoiceIn *hwi, *hwin;
|
||||||
|
|
||||||
|
|
@ -1664,17 +1676,24 @@ static void free_audio_state(AudioState *s)
|
||||||
s->ts = NULL;
|
s->ts = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(s);
|
if (s->vmse) {
|
||||||
|
qemu_del_vm_change_state_handler(s->vmse);
|
||||||
|
s->vmse = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vmstate_unregister(NULL, &vmstate_audio, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *get_audiodevs_root(void)
|
||||||
|
{
|
||||||
|
return object_get_container("audiodevs");
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_cleanup(void)
|
void audio_cleanup(void)
|
||||||
{
|
{
|
||||||
default_audio_state = NULL;
|
default_audio_be = NULL;
|
||||||
while (!QTAILQ_EMPTY(&audio_states)) {
|
|
||||||
AudioState *s = QTAILQ_FIRST(&audio_states);
|
object_unparent(get_audiodevs_root());
|
||||||
QTAILQ_REMOVE(&audio_states, s, list);
|
|
||||||
free_audio_state(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool vmstate_audio_needed(void *opaque)
|
static bool vmstate_audio_needed(void *opaque)
|
||||||
|
|
@ -1712,7 +1731,7 @@ void audio_create_default_audiodevs(void)
|
||||||
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
|
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
|
|
||||||
audio_define_default(dev, &error_abort);
|
audio_add_default_audiodev(dev, &error_abort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1723,26 +1742,14 @@ void audio_create_default_audiodevs(void)
|
||||||
* if dev == NULL => legacy implicit initialization, return the already created
|
* if dev == NULL => legacy implicit initialization, return the already created
|
||||||
* state or create a new one
|
* state or create a new one
|
||||||
*/
|
*/
|
||||||
static AudioState *audio_init(Audiodev *dev, Error **errp)
|
static AudioBackend *audio_init(Audiodev *dev, Error **errp)
|
||||||
{
|
{
|
||||||
static bool atexit_registered;
|
|
||||||
int done = 0;
|
int done = 0;
|
||||||
const char *drvname;
|
const char *drvname;
|
||||||
VMChangeStateEntry *vmse;
|
AudioBackend *s;
|
||||||
AudioState *s;
|
|
||||||
struct audio_driver *driver;
|
struct audio_driver *driver;
|
||||||
|
|
||||||
s = g_new0(AudioState, 1);
|
s = AUDIO_BACKEND(object_new(TYPE_AUDIO_BACKEND));
|
||||||
|
|
||||||
QLIST_INIT (&s->hw_head_out);
|
|
||||||
QLIST_INIT (&s->hw_head_in);
|
|
||||||
QLIST_INIT (&s->cap_head);
|
|
||||||
if (!atexit_registered) {
|
|
||||||
atexit(audio_cleanup);
|
|
||||||
atexit_registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
|
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
/* -audiodev option */
|
/* -audiodev option */
|
||||||
|
|
@ -1750,7 +1757,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
|
||||||
drvname = AudiodevDriver_str(dev->driver);
|
drvname = AudiodevDriver_str(dev->driver);
|
||||||
driver = audio_driver_lookup(drvname);
|
driver = audio_driver_lookup(drvname);
|
||||||
if (driver) {
|
if (driver) {
|
||||||
done = !audio_driver_init(s, driver, dev, errp);
|
done = audio_driver_init(s, driver, dev, errp);
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Unknown audio driver `%s'", drvname);
|
error_setg(errp, "Unknown audio driver `%s'", drvname);
|
||||||
}
|
}
|
||||||
|
|
@ -1758,7 +1765,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(!default_audio_state);
|
assert(!default_audio_be);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
|
AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
|
|
@ -1770,7 +1777,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
|
||||||
g_free(e);
|
g_free(e);
|
||||||
drvname = AudiodevDriver_str(dev->driver);
|
drvname = AudiodevDriver_str(dev->driver);
|
||||||
driver = audio_driver_lookup(drvname);
|
driver = audio_driver_lookup(drvname);
|
||||||
if (!audio_driver_init(s, driver, dev, NULL)) {
|
if (audio_driver_init(s, driver, dev, NULL)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
qapi_free_Audiodev(dev);
|
qapi_free_Audiodev(dev);
|
||||||
|
|
@ -1778,33 +1785,22 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->timer_period <= 0) {
|
if (!object_property_try_add_child(get_audiodevs_root(), dev->id, OBJECT(s), errp)) {
|
||||||
s->period_ticks = 1;
|
goto out;
|
||||||
} else {
|
|
||||||
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
|
|
||||||
}
|
}
|
||||||
|
object_unref(s);
|
||||||
vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
|
|
||||||
if (!vmse) {
|
|
||||||
dolog ("warning: Could not register change state handler\n"
|
|
||||||
"(Audio can continue looping even after stopping the VM)\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&audio_states, s, list);
|
|
||||||
QLIST_INIT (&s->card_head);
|
|
||||||
vmstate_register_any(NULL, &vmstate_audio, s);
|
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free_audio_state(s);
|
object_unref(s);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioState *audio_get_default_audio_state(Error **errp)
|
AudioBackend *audio_get_default_audio_be(Error **errp)
|
||||||
{
|
{
|
||||||
if (!default_audio_state) {
|
if (!default_audio_be) {
|
||||||
default_audio_state = audio_init(NULL, errp);
|
default_audio_be = audio_init(NULL, errp);
|
||||||
if (!default_audio_state) {
|
if (!default_audio_be) {
|
||||||
if (!QSIMPLEQ_EMPTY(&audiodevs)) {
|
if (!QSIMPLEQ_EMPTY(&audiodevs)) {
|
||||||
error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n",
|
error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n",
|
||||||
QSIMPLEQ_FIRST(&audiodevs)->dev->id);
|
QSIMPLEQ_FIRST(&audiodevs)->dev->id);
|
||||||
|
|
@ -1812,35 +1808,27 @@ AudioState *audio_get_default_audio_state(Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return default_audio_state;
|
return default_audio_be;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp)
|
bool AUD_backend_check(AudioBackend **be, Error **errp)
|
||||||
{
|
{
|
||||||
if (!card->state) {
|
assert(be != NULL);
|
||||||
card->state = audio_get_default_audio_state(errp);
|
|
||||||
if (!card->state) {
|
if (!*be) {
|
||||||
|
*be = audio_get_default_audio_be(errp);
|
||||||
|
if (!*be) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
card->name = g_strdup (name);
|
|
||||||
memset (&card->entries, 0, sizeof (card->entries));
|
|
||||||
QLIST_INSERT_HEAD(&card->state->card_head, card, entries);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUD_remove_card (QEMUSoundCard *card)
|
|
||||||
{
|
|
||||||
QLIST_REMOVE (card, entries);
|
|
||||||
g_free (card->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct audio_pcm_ops capture_pcm_ops;
|
static struct audio_pcm_ops capture_pcm_ops;
|
||||||
|
|
||||||
CaptureVoiceOut *AUD_add_capture(
|
CaptureVoiceOut *AUD_add_capture(
|
||||||
AudioState *s,
|
AudioBackend *s,
|
||||||
struct audsettings *as,
|
struct audsettings *as,
|
||||||
struct audio_capture_ops *ops,
|
struct audio_capture_ops *ops,
|
||||||
void *cb_opaque
|
void *cb_opaque
|
||||||
|
|
@ -1952,13 +1940,7 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
|
void AUD_set_volume_out(SWVoiceOut *sw, Volume *vol)
|
||||||
{
|
|
||||||
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
|
|
||||||
audio_set_volume_out(sw, &vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
|
|
||||||
{
|
{
|
||||||
if (sw) {
|
if (sw) {
|
||||||
HWVoiceOut *hw = sw->hw;
|
HWVoiceOut *hw = sw->hw;
|
||||||
|
|
@ -1974,13 +1956,7 @@ void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
|
void AUD_set_volume_in(SWVoiceIn *sw, Volume *vol)
|
||||||
{
|
|
||||||
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
|
|
||||||
audio_set_volume_in(sw, &vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol)
|
|
||||||
{
|
{
|
||||||
if (sw) {
|
if (sw) {
|
||||||
HWVoiceIn *hw = sw->hw;
|
HWVoiceIn *hw = sw->hw;
|
||||||
|
|
@ -2142,10 +2118,10 @@ void audio_parse_option(const char *opt)
|
||||||
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
|
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
|
|
||||||
audio_define(dev);
|
audio_add_audiodev(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_define(Audiodev *dev)
|
void audio_add_audiodev(Audiodev *dev)
|
||||||
{
|
{
|
||||||
AudiodevListEntry *e;
|
AudiodevListEntry *e;
|
||||||
|
|
||||||
|
|
@ -2156,7 +2132,7 @@ void audio_define(Audiodev *dev)
|
||||||
QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
|
QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_define_default(Audiodev *dev, Error **errp)
|
void audio_add_default_audiodev(Audiodev *dev, Error **errp)
|
||||||
{
|
{
|
||||||
AudiodevListEntry *e;
|
AudiodevListEntry *e;
|
||||||
|
|
||||||
|
|
@ -2182,7 +2158,7 @@ audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
|
||||||
.freq = pdo->frequency,
|
.freq = pdo->frequency,
|
||||||
.nchannels = pdo->channels,
|
.nchannels = pdo->channels,
|
||||||
.fmt = pdo->format,
|
.fmt = pdo->format,
|
||||||
.endianness = AUDIO_HOST_ENDIANNESS,
|
.endianness = HOST_BIG_ENDIAN,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2235,24 +2211,40 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
|
||||||
audioformat_bytes_per_sample(as->fmt);
|
audioformat_bytes_per_sample(as->fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioState *audio_state_by_name(const char *name, Error **errp)
|
AudioBackend *audio_be_by_name(const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
AudioState *s;
|
Object *obj = object_resolve_path_component(get_audiodevs_root(), name);
|
||||||
QTAILQ_FOREACH(s, &audio_states, list) {
|
|
||||||
assert(s->dev);
|
if (!obj) {
|
||||||
if (strcmp(name, s->dev->id) == 0) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error_setg(errp, "audiodev '%s' not found", name);
|
error_setg(errp, "audiodev '%s' not found", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return AUDIO_BACKEND(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *audio_get_id(QEMUSoundCard *card)
|
#ifdef CONFIG_GIO
|
||||||
|
bool audio_be_set_dbus_server(AudioBackend *be,
|
||||||
|
GDBusObjectManagerServer *server,
|
||||||
|
bool p2p,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
if (card->state) {
|
assert(be != NULL);
|
||||||
assert(card->state->dev);
|
|
||||||
return card->state->dev->id;
|
if (!be->drv->set_dbus_server) {
|
||||||
|
error_setg(errp, "Audiodev '%s' is not compatible with DBus", be->dev->id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return be->drv->set_dbus_server(be, server, p2p, errp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *audio_be_get_id(AudioBackend *be)
|
||||||
|
{
|
||||||
|
if (be) {
|
||||||
|
assert(be->dev);
|
||||||
|
return be->dev->id;
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -2320,3 +2312,20 @@ AudiodevList *qmp_query_audiodevs(Error **errp)
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const TypeInfo audio_be_info = {
|
||||||
|
.name = TYPE_AUDIO_BACKEND,
|
||||||
|
.parent = TYPE_OBJECT,
|
||||||
|
.instance_size = sizeof(AudioBackend),
|
||||||
|
.instance_init = audio_be_init,
|
||||||
|
.instance_finalize = audio_be_finalize,
|
||||||
|
.abstract = false, /* TODO: subclass drivers and make it abstract */
|
||||||
|
.class_size = sizeof(AudioBackendClass),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&audio_be_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(register_types);
|
||||||
|
|
|
||||||
185
audio/audio.h
185
audio/audio.h
|
|
@ -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 */
|
|
||||||
|
|
@ -29,12 +29,20 @@
|
||||||
#define FLOAT_MIXENG
|
#define FLOAT_MIXENG
|
||||||
/* #define RECIPROCAL */
|
/* #define RECIPROCAL */
|
||||||
#endif
|
#endif
|
||||||
|
#include "qemu/audio.h"
|
||||||
|
#include "qemu/audio-capture.h"
|
||||||
#include "mixeng.h"
|
#include "mixeng.h"
|
||||||
|
|
||||||
#ifdef CONFIG_GIO
|
#ifdef CONFIG_GIO
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void G_GNUC_PRINTF(2, 0)
|
||||||
|
AUD_vlog(const char *cap, const char *fmt, va_list ap);
|
||||||
|
|
||||||
|
void G_GNUC_PRINTF(2, 3)
|
||||||
|
AUD_log(const char *cap, const char *fmt, ...);
|
||||||
|
|
||||||
struct audio_pcm_ops;
|
struct audio_pcm_ops;
|
||||||
|
|
||||||
struct audio_callback {
|
struct audio_callback {
|
||||||
|
|
@ -53,7 +61,7 @@ struct audio_pcm_info {
|
||||||
int swap_endianness;
|
int swap_endianness;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct AudioState AudioState;
|
typedef struct AudioBackend AudioBackend;
|
||||||
typedef struct SWVoiceCap SWVoiceCap;
|
typedef struct SWVoiceCap SWVoiceCap;
|
||||||
|
|
||||||
typedef struct STSampleBuffer {
|
typedef struct STSampleBuffer {
|
||||||
|
|
@ -62,10 +70,10 @@ typedef struct STSampleBuffer {
|
||||||
} STSampleBuffer;
|
} STSampleBuffer;
|
||||||
|
|
||||||
typedef struct HWVoiceOut {
|
typedef struct HWVoiceOut {
|
||||||
AudioState *s;
|
AudioBackend *s;
|
||||||
int enabled;
|
bool enabled;
|
||||||
int poll_mode;
|
int poll_mode;
|
||||||
int pending_disable;
|
bool pending_disable;
|
||||||
struct audio_pcm_info info;
|
struct audio_pcm_info info;
|
||||||
|
|
||||||
f_sample *clip;
|
f_sample *clip;
|
||||||
|
|
@ -83,8 +91,8 @@ typedef struct HWVoiceOut {
|
||||||
} HWVoiceOut;
|
} HWVoiceOut;
|
||||||
|
|
||||||
typedef struct HWVoiceIn {
|
typedef struct HWVoiceIn {
|
||||||
AudioState *s;
|
AudioBackend *s;
|
||||||
int enabled;
|
bool enabled;
|
||||||
int poll_mode;
|
int poll_mode;
|
||||||
struct audio_pcm_info info;
|
struct audio_pcm_info info;
|
||||||
|
|
||||||
|
|
@ -104,15 +112,14 @@ typedef struct HWVoiceIn {
|
||||||
} HWVoiceIn;
|
} HWVoiceIn;
|
||||||
|
|
||||||
struct SWVoiceOut {
|
struct SWVoiceOut {
|
||||||
QEMUSoundCard *card;
|
AudioBackend *s;
|
||||||
AudioState *s;
|
|
||||||
struct audio_pcm_info info;
|
struct audio_pcm_info info;
|
||||||
t_sample *conv;
|
t_sample *conv;
|
||||||
STSampleBuffer resample_buf;
|
STSampleBuffer resample_buf;
|
||||||
void *rate;
|
void *rate;
|
||||||
size_t total_hw_samples_mixed;
|
size_t total_hw_samples_mixed;
|
||||||
int active;
|
bool active;
|
||||||
int empty;
|
bool empty;
|
||||||
HWVoiceOut *hw;
|
HWVoiceOut *hw;
|
||||||
char *name;
|
char *name;
|
||||||
struct mixeng_volume vol;
|
struct mixeng_volume vol;
|
||||||
|
|
@ -121,9 +128,8 @@ struct SWVoiceOut {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SWVoiceIn {
|
struct SWVoiceIn {
|
||||||
QEMUSoundCard *card;
|
AudioBackend *s;
|
||||||
AudioState *s;
|
bool active;
|
||||||
int active;
|
|
||||||
struct audio_pcm_info info;
|
struct audio_pcm_info info;
|
||||||
void *rate;
|
void *rate;
|
||||||
size_t total_hw_samples_acquired;
|
size_t total_hw_samples_acquired;
|
||||||
|
|
@ -139,11 +145,13 @@ struct SWVoiceIn {
|
||||||
typedef struct audio_driver audio_driver;
|
typedef struct audio_driver audio_driver;
|
||||||
struct audio_driver {
|
struct audio_driver {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *descr;
|
|
||||||
void *(*init) (Audiodev *, Error **);
|
void *(*init) (Audiodev *, Error **);
|
||||||
void (*fini) (void *);
|
void (*fini) (void *);
|
||||||
#ifdef CONFIG_GIO
|
#ifdef CONFIG_GIO
|
||||||
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager, bool p2p);
|
bool (*set_dbus_server)(AudioBackend *be,
|
||||||
|
GDBusObjectManagerServer *manager,
|
||||||
|
bool p2p,
|
||||||
|
Error **errp);
|
||||||
#endif
|
#endif
|
||||||
struct audio_pcm_ops *pcm_ops;
|
struct audio_pcm_ops *pcm_ops;
|
||||||
int max_voices_out;
|
int max_voices_out;
|
||||||
|
|
@ -187,6 +195,23 @@ struct audio_pcm_ops {
|
||||||
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
|
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
|
||||||
|
int audioformat_bytes_per_sample(AudioFormat fmt);
|
||||||
|
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
|
||||||
|
audsettings *as, int def_usecs);
|
||||||
|
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
|
||||||
|
audsettings *as, int def_usecs);
|
||||||
|
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
|
||||||
|
audsettings *as, int def_usecs);
|
||||||
|
|
||||||
|
static inline void *advance(void *p, size_t incr)
|
||||||
|
{
|
||||||
|
return (uint8_t *)p + incr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wav_start_capture(AudioBackend *state, CaptureState *s, const char *path,
|
||||||
|
int freq, int bits, int nchannels);
|
||||||
|
|
||||||
void audio_generic_run_buffer_in(HWVoiceIn *hw);
|
void audio_generic_run_buffer_in(HWVoiceIn *hw);
|
||||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||||
|
|
@ -216,13 +241,14 @@ struct SWVoiceCap {
|
||||||
QLIST_ENTRY (SWVoiceCap) entries;
|
QLIST_ENTRY (SWVoiceCap) entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct AudioState {
|
typedef struct AudioBackend {
|
||||||
|
Object parent;
|
||||||
|
|
||||||
struct audio_driver *drv;
|
struct audio_driver *drv;
|
||||||
Audiodev *dev;
|
Audiodev *dev;
|
||||||
void *drv_opaque;
|
void *drv_opaque;
|
||||||
|
|
||||||
QEMUTimer *ts;
|
QEMUTimer *ts;
|
||||||
QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
|
|
||||||
QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
|
QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
|
||||||
QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
|
QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
|
||||||
QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
|
QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
|
||||||
|
|
@ -233,9 +259,8 @@ typedef struct AudioState {
|
||||||
|
|
||||||
bool timer_running;
|
bool timer_running;
|
||||||
uint64_t timer_last;
|
uint64_t timer_last;
|
||||||
|
VMChangeStateEntry *vmse;
|
||||||
QTAILQ_ENTRY(AudioState) list;
|
} AudioBackend;
|
||||||
} AudioState;
|
|
||||||
|
|
||||||
extern const struct mixeng_volume nominal_volume;
|
extern const struct mixeng_volume nominal_volume;
|
||||||
|
|
||||||
|
|
@ -248,7 +273,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
|
||||||
|
|
||||||
int audio_bug (const char *funcname, int cond);
|
int audio_bug (const char *funcname, int cond);
|
||||||
|
|
||||||
void audio_run(AudioState *s, const char *msg);
|
void audio_run(AudioBackend *s, const char *msg);
|
||||||
|
|
||||||
const char *audio_application_name(void);
|
const char *audio_application_name(void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
#define HWBUF hw->conv_buf
|
#define HWBUF hw->conv_buf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
static void glue(audio_init_nb_voices_, TYPE)(AudioBackend *s,
|
||||||
struct audio_driver *drv, int min_voices)
|
struct audio_driver *drv, int min_voices)
|
||||||
{
|
{
|
||||||
int max_voices = glue (drv->max_voices_, TYPE);
|
int max_voices = glue (drv->max_voices_, TYPE);
|
||||||
|
|
@ -166,10 +166,10 @@ static int glue (audio_pcm_sw_init_, TYPE) (
|
||||||
|
|
||||||
audio_pcm_init_info (&sw->info, as);
|
audio_pcm_init_info (&sw->info, as);
|
||||||
sw->hw = hw;
|
sw->hw = hw;
|
||||||
sw->active = 0;
|
sw->active = false;
|
||||||
#ifdef DAC
|
#ifdef DAC
|
||||||
sw->total_hw_samples_mixed = 0;
|
sw->total_hw_samples_mixed = 0;
|
||||||
sw->empty = 1;
|
sw->empty = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sw->info.is_float) {
|
if (sw->info.is_float) {
|
||||||
|
|
@ -221,7 +221,7 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
|
||||||
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
|
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
|
||||||
{
|
{
|
||||||
HW *hw = *hwp;
|
HW *hw = *hwp;
|
||||||
AudioState *s = hw->s;
|
AudioBackend *s = hw->s;
|
||||||
|
|
||||||
if (!hw->sw_head.lh_first) {
|
if (!hw->sw_head.lh_first) {
|
||||||
#ifdef DAC
|
#ifdef DAC
|
||||||
|
|
@ -236,12 +236,12 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
|
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioBackend *s, HW *hw)
|
||||||
{
|
{
|
||||||
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
|
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
|
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioBackend *s, HW *hw)
|
||||||
{
|
{
|
||||||
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
|
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
|
||||||
if (hw->enabled) {
|
if (hw->enabled) {
|
||||||
|
|
@ -251,7 +251,7 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
|
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioBackend *s, HW *hw,
|
||||||
struct audsettings *as)
|
struct audsettings *as)
|
||||||
{
|
{
|
||||||
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
|
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
|
||||||
|
|
@ -262,7 +262,7 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioBackend *s,
|
||||||
struct audsettings *as)
|
struct audsettings *as)
|
||||||
{
|
{
|
||||||
HW *hw;
|
HW *hw;
|
||||||
|
|
@ -398,7 +398,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
|
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioBackend *s, struct audsettings *as)
|
||||||
{
|
{
|
||||||
HW *hw;
|
HW *hw;
|
||||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
@ -424,7 +424,7 @@ static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
|
||||||
}
|
}
|
||||||
|
|
||||||
static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
|
static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
|
||||||
AudioState *s,
|
AudioBackend *s,
|
||||||
const char *sw_name,
|
const char *sw_name,
|
||||||
struct audsettings *as
|
struct audsettings *as
|
||||||
)
|
)
|
||||||
|
|
@ -473,11 +473,11 @@ static void glue (audio_close_, TYPE) (SW *sw)
|
||||||
g_free (sw);
|
g_free (sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
|
void glue(AUD_close_, TYPE)(AudioBackend *be, SW *sw)
|
||||||
{
|
{
|
||||||
if (sw) {
|
if (sw) {
|
||||||
if (audio_bug(__func__, !card)) {
|
if (audio_bug(__func__, !be)) {
|
||||||
dolog ("card=%p\n", card);
|
dolog("backend=%p\n", be);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -486,7 +486,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
|
||||||
}
|
}
|
||||||
|
|
||||||
SW *glue (AUD_open_, TYPE) (
|
SW *glue (AUD_open_, TYPE) (
|
||||||
QEMUSoundCard *card,
|
AudioBackend *be,
|
||||||
SW *sw,
|
SW *sw,
|
||||||
const char *name,
|
const char *name,
|
||||||
void *callback_opaque ,
|
void *callback_opaque ,
|
||||||
|
|
@ -494,16 +494,15 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
struct audsettings *as
|
struct audsettings *as
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
AudioState *s;
|
AudioBackend *s = be;
|
||||||
AudiodevPerDirectionOptions *pdo;
|
AudiodevPerDirectionOptions *pdo;
|
||||||
|
|
||||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
if (audio_bug(__func__, !be || !name || !callback_fn || !as)) {
|
||||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
dolog("backend=%p name=%p callback_fn=%p as=%p\n",
|
||||||
card, name, callback_fn, as);
|
be, name, callback_fn, as);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = card->state;
|
|
||||||
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
||||||
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
||||||
|
|
@ -524,7 +523,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pdo->fixed_settings && sw) {
|
if (!pdo->fixed_settings && sw) {
|
||||||
glue (AUD_close_, TYPE) (card, sw);
|
glue(AUD_close_, TYPE)(be, sw);
|
||||||
sw = NULL;
|
sw = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -548,7 +547,6 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sw->card = card;
|
|
||||||
sw->vol = nominal_volume;
|
sw->vol = nominal_volume;
|
||||||
sw->callback.fn = callback_fn;
|
sw->callback.fn = callback_fn;
|
||||||
sw->callback.opaque = callback_opaque;
|
sw->callback.opaque = callback_opaque;
|
||||||
|
|
@ -562,11 +560,11 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
return sw;
|
return sw;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
glue (AUD_close_, TYPE) (card, sw);
|
glue(AUD_close_, TYPE)(be, sw);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int glue (AUD_is_active_, TYPE) (SW *sw)
|
bool glue(AUD_is_active_, TYPE)(SW *sw)
|
||||||
{
|
{
|
||||||
return sw ? sw->active : 0;
|
return sw ? sw->active : 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include <mmreg.h>
|
#include <mmreg.h>
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
#include "audio_win_int.h"
|
#include "audio_win_int.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "coreaudio"
|
#define AUDIO_CAP "coreaudio"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
@ -664,7 +664,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver coreaudio_audio_driver = {
|
static struct audio_driver coreaudio_audio_driver = {
|
||||||
.name = "coreaudio",
|
.name = "coreaudio",
|
||||||
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
|
||||||
.init = coreaudio_audio_init,
|
.init = coreaudio_audio_init,
|
||||||
.fini = coreaudio_audio_fini,
|
.fini = coreaudio_audio_fini,
|
||||||
.pcm_ops = &coreaudio_pcm_ops,
|
.pcm_ops = &coreaudio_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,7 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/host-utils.h"
|
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/timer.h"
|
|
||||||
#include "qemu/dbus.h"
|
#include "qemu/dbus.h"
|
||||||
|
|
||||||
#ifdef G_OS_UNIX
|
#ifdef G_OS_UNIX
|
||||||
|
|
@ -37,7 +35,7 @@
|
||||||
#include "ui/dbus-display1.h"
|
#include "ui/dbus-display1.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "dbus"
|
#define AUDIO_CAP "dbus"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
|
@ -460,7 +458,7 @@ listener_in_vanished_cb(GDBusConnection *connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
dbus_audio_register_listener(AudioState *s,
|
dbus_audio_register_listener(AudioBackend *s,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
#ifdef G_OS_UNIX
|
#ifdef G_OS_UNIX
|
||||||
GUnixFDList *fd_list,
|
GUnixFDList *fd_list,
|
||||||
|
|
@ -617,7 +615,7 @@ dbus_audio_register_listener(AudioState *s,
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
dbus_audio_register_out_listener(AudioState *s,
|
dbus_audio_register_out_listener(AudioBackend *s,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
#ifdef G_OS_UNIX
|
#ifdef G_OS_UNIX
|
||||||
GUnixFDList *fd_list,
|
GUnixFDList *fd_list,
|
||||||
|
|
@ -633,7 +631,7 @@ dbus_audio_register_out_listener(AudioState *s,
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
dbus_audio_register_in_listener(AudioState *s,
|
dbus_audio_register_in_listener(AudioBackend *s,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
#ifdef G_OS_UNIX
|
#ifdef G_OS_UNIX
|
||||||
GUnixFDList *fd_list,
|
GUnixFDList *fd_list,
|
||||||
|
|
@ -647,8 +645,11 @@ dbus_audio_register_in_listener(AudioState *s,
|
||||||
arg_listener, false);
|
arg_listener, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
|
dbus_audio_set_server(AudioBackend *s,
|
||||||
|
GDBusObjectManagerServer *server,
|
||||||
|
bool p2p,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
DBusAudio *da = s->drv_opaque;
|
DBusAudio *da = s->drv_opaque;
|
||||||
|
|
||||||
|
|
@ -671,6 +672,8 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
|
||||||
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
|
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
|
||||||
G_DBUS_INTERFACE_SKELETON(da->iface));
|
G_DBUS_INTERFACE_SKELETON(da->iface));
|
||||||
g_dbus_object_manager_server_export(da->server, da->audio);
|
g_dbus_object_manager_server_export(da->server, da->audio);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_pcm_ops dbus_pcm_ops = {
|
static struct audio_pcm_ops dbus_pcm_ops = {
|
||||||
|
|
@ -692,7 +695,6 @@ static struct audio_pcm_ops dbus_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver dbus_audio_driver = {
|
static struct audio_driver dbus_audio_driver = {
|
||||||
.name = "dbus",
|
.name = "dbus",
|
||||||
.descr = "Timer based audio exposed with DBus interface",
|
|
||||||
.init = dbus_audio_init,
|
.init = dbus_audio_init,
|
||||||
.fini = dbus_audio_fini,
|
.fini = dbus_audio_fini,
|
||||||
.set_dbus_server = dbus_audio_set_server,
|
.set_dbus_server = dbus_audio_set_server,
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "dsound"
|
#define AUDIO_CAP "dsound"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
#include "qemu/host-utils.h"
|
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
|
@ -64,162 +64,154 @@ typedef struct {
|
||||||
dsound *s;
|
dsound *s;
|
||||||
} DSoundVoiceIn;
|
} DSoundVoiceIn;
|
||||||
|
|
||||||
static void dsound_log_hresult (HRESULT hr)
|
static const char *dserror(HRESULT hr)
|
||||||
{
|
{
|
||||||
const char *str = "BUG";
|
|
||||||
|
|
||||||
switch (hr) {
|
switch (hr) {
|
||||||
case DS_OK:
|
case DS_OK:
|
||||||
str = "The method succeeded";
|
return "The method succeeded";
|
||||||
break;
|
|
||||||
#ifdef DS_NO_VIRTUALIZATION
|
#ifdef DS_NO_VIRTUALIZATION
|
||||||
case DS_NO_VIRTUALIZATION:
|
case DS_NO_VIRTUALIZATION:
|
||||||
str = "The buffer was created, but another 3D algorithm was substituted";
|
return "The buffer was created, but another 3D algorithm was substituted";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DS_INCOMPLETE
|
#ifdef DS_INCOMPLETE
|
||||||
case DS_INCOMPLETE:
|
case DS_INCOMPLETE:
|
||||||
str = "The method succeeded, but not all the optional effects were obtained";
|
return "The method succeeded, but not all the optional effects were obtained";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_ACCESSDENIED
|
#ifdef DSERR_ACCESSDENIED
|
||||||
case DSERR_ACCESSDENIED:
|
case DSERR_ACCESSDENIED:
|
||||||
str = "The request failed because access was denied";
|
return "The request failed because access was denied";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_ALLOCATED
|
#ifdef DSERR_ALLOCATED
|
||||||
case DSERR_ALLOCATED:
|
case DSERR_ALLOCATED:
|
||||||
str = "The request failed because resources, "
|
return "The request failed because resources, "
|
||||||
"such as a priority level, were already in use "
|
"such as a priority level, were already in use "
|
||||||
"by another caller";
|
"by another caller";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_ALREADYINITIALIZED
|
#ifdef DSERR_ALREADYINITIALIZED
|
||||||
case DSERR_ALREADYINITIALIZED:
|
case DSERR_ALREADYINITIALIZED:
|
||||||
str = "The object is already initialized";
|
return "The object is already initialized";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_BADFORMAT
|
#ifdef DSERR_BADFORMAT
|
||||||
case DSERR_BADFORMAT:
|
case DSERR_BADFORMAT:
|
||||||
str = "The specified wave format is not supported";
|
return "The specified wave format is not supported";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_BADSENDBUFFERGUID
|
#ifdef DSERR_BADSENDBUFFERGUID
|
||||||
case DSERR_BADSENDBUFFERGUID:
|
case DSERR_BADSENDBUFFERGUID:
|
||||||
str = "The GUID specified in an audiopath file "
|
return "The GUID specified in an audiopath file "
|
||||||
"does not match a valid mix-in buffer";
|
"does not match a valid mix-in buffer";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_BUFFERLOST
|
#ifdef DSERR_BUFFERLOST
|
||||||
case DSERR_BUFFERLOST:
|
case DSERR_BUFFERLOST:
|
||||||
str = "The buffer memory has been lost and must be restored";
|
return "The buffer memory has been lost and must be restored";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_BUFFERTOOSMALL
|
#ifdef DSERR_BUFFERTOOSMALL
|
||||||
case DSERR_BUFFERTOOSMALL:
|
case DSERR_BUFFERTOOSMALL:
|
||||||
str = "The buffer size is not great enough to "
|
return "The buffer size is not great enough to "
|
||||||
"enable effects processing";
|
"enable effects processing";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_CONTROLUNAVAIL
|
#ifdef DSERR_CONTROLUNAVAIL
|
||||||
case DSERR_CONTROLUNAVAIL:
|
case DSERR_CONTROLUNAVAIL:
|
||||||
str = "The buffer control (volume, pan, and so on) "
|
return "The buffer control (volume, pan, and so on) "
|
||||||
"requested by the caller is not available. "
|
"requested by the caller is not available. "
|
||||||
"Controls must be specified when the buffer is created, "
|
"Controls must be specified when the buffer is created, "
|
||||||
"using the dwFlags member of DSBUFFERDESC";
|
"using the dwFlags member of DSBUFFERDESC";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_DS8_REQUIRED
|
#ifdef DSERR_DS8_REQUIRED
|
||||||
case DSERR_DS8_REQUIRED:
|
case DSERR_DS8_REQUIRED:
|
||||||
str = "A DirectSound object of class CLSID_DirectSound8 or later "
|
return "A DirectSound object of class CLSID_DirectSound8 or later "
|
||||||
"is required for the requested functionality. "
|
"is required for the requested functionality. "
|
||||||
"For more information, see IDirectSound8 Interface";
|
"For more information, see IDirectSound8 Interface";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_FXUNAVAILABLE
|
#ifdef DSERR_FXUNAVAILABLE
|
||||||
case DSERR_FXUNAVAILABLE:
|
case DSERR_FXUNAVAILABLE:
|
||||||
str = "The effects requested could not be found on the system, "
|
return "The effects requested could not be found on the system, "
|
||||||
"or they are in the wrong order or in the wrong location; "
|
"or they are in the wrong order or in the wrong location; "
|
||||||
"for example, an effect expected in hardware "
|
"for example, an effect expected in hardware "
|
||||||
"was found in software";
|
"was found in software";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_GENERIC
|
#ifdef DSERR_GENERIC
|
||||||
case DSERR_GENERIC:
|
case DSERR_GENERIC:
|
||||||
str = "An undetermined error occurred inside the DirectSound subsystem";
|
return "An undetermined error occurred inside the DirectSound subsystem";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_INVALIDCALL
|
#ifdef DSERR_INVALIDCALL
|
||||||
case DSERR_INVALIDCALL:
|
case DSERR_INVALIDCALL:
|
||||||
str = "This function is not valid for the current state of this object";
|
return "This function is not valid for the current state of this object";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_INVALIDPARAM
|
#ifdef DSERR_INVALIDPARAM
|
||||||
case DSERR_INVALIDPARAM:
|
case DSERR_INVALIDPARAM:
|
||||||
str = "An invalid parameter was passed to the returning function";
|
return "An invalid parameter was passed to the returning function";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_NOAGGREGATION
|
#ifdef DSERR_NOAGGREGATION
|
||||||
case DSERR_NOAGGREGATION:
|
case DSERR_NOAGGREGATION:
|
||||||
str = "The object does not support aggregation";
|
return "The object does not support aggregation";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_NODRIVER
|
#ifdef DSERR_NODRIVER
|
||||||
case DSERR_NODRIVER:
|
case DSERR_NODRIVER:
|
||||||
str = "No sound driver is available for use, "
|
return "No sound driver is available for use, "
|
||||||
"or the given GUID is not a valid DirectSound device ID";
|
"or the given GUID is not a valid DirectSound device ID";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_NOINTERFACE
|
#ifdef DSERR_NOINTERFACE
|
||||||
case DSERR_NOINTERFACE:
|
case DSERR_NOINTERFACE:
|
||||||
str = "The requested COM interface is not available";
|
return "The requested COM interface is not available";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_OBJECTNOTFOUND
|
#ifdef DSERR_OBJECTNOTFOUND
|
||||||
case DSERR_OBJECTNOTFOUND:
|
case DSERR_OBJECTNOTFOUND:
|
||||||
str = "The requested object was not found";
|
return "The requested object was not found";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_OTHERAPPHASPRIO
|
#ifdef DSERR_OTHERAPPHASPRIO
|
||||||
case DSERR_OTHERAPPHASPRIO:
|
case DSERR_OTHERAPPHASPRIO:
|
||||||
str = "Another application has a higher priority level, "
|
return "Another application has a higher priority level, "
|
||||||
"preventing this call from succeeding";
|
"preventing this call from succeeding";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_OUTOFMEMORY
|
#ifdef DSERR_OUTOFMEMORY
|
||||||
case DSERR_OUTOFMEMORY:
|
case DSERR_OUTOFMEMORY:
|
||||||
str = "The DirectSound subsystem could not allocate "
|
return "The DirectSound subsystem could not allocate "
|
||||||
"sufficient memory to complete the caller's request";
|
"sufficient memory to complete the caller's request";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_PRIOLEVELNEEDED
|
#ifdef DSERR_PRIOLEVELNEEDED
|
||||||
case DSERR_PRIOLEVELNEEDED:
|
case DSERR_PRIOLEVELNEEDED:
|
||||||
str = "A cooperative level of DSSCL_PRIORITY or higher is required";
|
return "A cooperative level of DSSCL_PRIORITY or higher is required";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_SENDLOOP
|
#ifdef DSERR_SENDLOOP
|
||||||
case DSERR_SENDLOOP:
|
case DSERR_SENDLOOP:
|
||||||
str = "A circular loop of send effects was detected";
|
return "A circular loop of send effects was detected";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_UNINITIALIZED
|
#ifdef DSERR_UNINITIALIZED
|
||||||
case DSERR_UNINITIALIZED:
|
case DSERR_UNINITIALIZED:
|
||||||
str = "The Initialize method has not been called "
|
return "The Initialize method has not been called "
|
||||||
"or has not been called successfully "
|
"or has not been called successfully "
|
||||||
"before other methods were called";
|
"before other methods were called";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef DSERR_UNSUPPORTED
|
#ifdef DSERR_UNSUPPORTED
|
||||||
case DSERR_UNSUPPORTED:
|
case DSERR_UNSUPPORTED:
|
||||||
str = "The function called is not supported at this time";
|
return "The function called is not supported at this time";
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr);
|
return NULL;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dserror_set(Error **errp, HRESULT hr, const char *msg)
|
||||||
|
{
|
||||||
|
const char *str = dserror(hr);
|
||||||
|
|
||||||
|
if (str) {
|
||||||
|
error_setg(errp, "%s: %s", msg, str);
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "%s: Unknown (HRESULT: 0x%lx)", msg, hr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsound_log_hresult(HRESULT hr)
|
||||||
|
{
|
||||||
|
const char *str = dserror(hr);
|
||||||
|
|
||||||
|
if (str) {
|
||||||
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
|
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
|
||||||
|
} else {
|
||||||
|
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT: 0x%lx)\n", hr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void G_GNUC_PRINTF (2, 3) dsound_logerr (
|
static void G_GNUC_PRINTF (2, 3) dsound_logerr (
|
||||||
|
|
@ -359,27 +351,6 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||||
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
|
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dsound_set_cooperative_level(dsound *s)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
HWND hwnd;
|
|
||||||
|
|
||||||
hwnd = GetDesktopWindow();
|
|
||||||
hr = IDirectSound_SetCooperativeLevel (
|
|
||||||
s->dsound,
|
|
||||||
hwnd,
|
|
||||||
DSSCL_PRIORITY
|
|
||||||
);
|
|
||||||
|
|
||||||
if (FAILED (hr)) {
|
|
||||||
dsound_logerr (hr, "Could not set cooperative level for window %p\n",
|
|
||||||
hwnd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
@ -621,7 +592,6 @@ static void dsound_audio_fini (void *opaque)
|
||||||
|
|
||||||
static void *dsound_audio_init(Audiodev *dev, Error **errp)
|
static void *dsound_audio_init(Audiodev *dev, Error **errp)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
dsound *s = g_new0(dsound, 1);
|
dsound *s = g_new0(dsound, 1);
|
||||||
AudiodevDsoundOptions *dso;
|
AudiodevDsoundOptions *dso;
|
||||||
|
|
@ -637,8 +607,8 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
|
||||||
|
|
||||||
hr = CoInitialize (NULL);
|
hr = CoInitialize (NULL);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not initialize COM\n");
|
dserror_set(errp, hr, "Could not initialize COM");
|
||||||
g_free(s);
|
dsound_audio_fini(s);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -650,20 +620,15 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
|
||||||
(void **) &s->dsound
|
(void **) &s->dsound
|
||||||
);
|
);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not create DirectSound instance\n");
|
dserror_set(errp, hr, "Could not create DirectSound instance");
|
||||||
g_free(s);
|
dsound_audio_fini(s);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = IDirectSound_Initialize (s->dsound, NULL);
|
hr = IDirectSound_Initialize (s->dsound, NULL);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not initialize DirectSound\n");
|
dserror_set(errp, hr, "Could not initialize DirectSound");
|
||||||
|
dsound_audio_fini(s);
|
||||||
hr = IDirectSound_Release (s->dsound);
|
|
||||||
if (FAILED (hr)) {
|
|
||||||
dsound_logerr (hr, "Could not release DirectSound\n");
|
|
||||||
}
|
|
||||||
g_free(s);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,22 +640,25 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
|
||||||
(void **) &s->dsound_capture
|
(void **) &s->dsound_capture
|
||||||
);
|
);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
|
dserror_set(errp, hr, "Could not create DirectSoundCapture instance");
|
||||||
} else {
|
dsound_audio_fini(s);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
|
hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
|
dserror_set(errp, hr, "Could not initialize DirectSoundCapture");
|
||||||
|
dsound_audio_fini(s);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
hr = IDirectSoundCapture_Release (s->dsound_capture);
|
hr = IDirectSound_SetCooperativeLevel (
|
||||||
|
s->dsound,
|
||||||
|
GetDesktopWindow(),
|
||||||
|
DSSCL_PRIORITY
|
||||||
|
);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dsound_logerr (hr, "Could not release DirectSoundCapture\n");
|
dserror_set(errp, hr, "Could not set cooperative level");
|
||||||
}
|
|
||||||
s->dsound_capture = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dsound_set_cooperative_level(s);
|
|
||||||
if (err) {
|
|
||||||
dsound_audio_fini(s);
|
dsound_audio_fini(s);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -717,7 +685,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver dsound_audio_driver = {
|
static struct audio_driver dsound_audio_driver = {
|
||||||
.name = "dsound",
|
.name = "dsound",
|
||||||
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
|
|
||||||
.init = dsound_audio_init,
|
.init = dsound_audio_init,
|
||||||
.fini = dsound_audio_fini,
|
.fini = dsound_audio_fini,
|
||||||
.pcm_ops = &dsound_pcm_ops,
|
.pcm_ops = &dsound_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/atomic.h"
|
#include "qemu/atomic.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "jack"
|
#define AUDIO_CAP "jack"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
@ -672,7 +672,6 @@ static struct audio_pcm_ops jack_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver jack_driver = {
|
static struct audio_driver jack_driver = {
|
||||||
.name = "jack",
|
.name = "jack",
|
||||||
.descr = "JACK Audio Connection Kit Client",
|
|
||||||
.init = qjack_init,
|
.init = qjack_init,
|
||||||
.fini = qjack_fini,
|
.fini = qjack_fini,
|
||||||
.pcm_ops = &jack_pcm_ops,
|
.pcm_ops = &jack_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
system_ss.add([spice_headers, files('audio.c')])
|
|
||||||
system_ss.add(files(
|
system_ss.add(files(
|
||||||
'audio-hmp-cmds.c',
|
'audio.c',
|
||||||
'mixeng.c',
|
'mixeng.c',
|
||||||
'noaudio.c',
|
'noaudio.c',
|
||||||
'wavaudio.c',
|
'wavaudio.c',
|
||||||
'wavcapture.c',
|
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# deprecated since v10.2, to be removed
|
||||||
|
system_ss.add(files('audio-hmp-cmds.c', 'wavcapture.c'))
|
||||||
|
|
||||||
system_ss.add(when: coreaudio, if_true: files('coreaudio.m'))
|
system_ss.add(when: coreaudio, if_true: files('coreaudio.m'))
|
||||||
system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))
|
system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,13 @@
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/audio.h"
|
||||||
#include "audio.h"
|
|
||||||
|
|
||||||
#define AUDIO_CAP "mixeng"
|
#define AUDIO_CAP "mixeng"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
#ifdef FLOAT_MIXENG
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* 8 bit */
|
/* 8 bit */
|
||||||
#define ENDIAN_CONVERSION natural
|
#define ENDIAN_CONVERSION natural
|
||||||
|
|
@ -402,7 +404,7 @@ f_sample *mixeng_clip_float[2][2] = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void audio_sample_to_uint64(const void *samples, int pos,
|
void audio_sample_to_uint64(const st_sample *sample, int pos,
|
||||||
uint64_t *left, uint64_t *right)
|
uint64_t *left, uint64_t *right)
|
||||||
{
|
{
|
||||||
#ifdef FLOAT_MIXENG
|
#ifdef FLOAT_MIXENG
|
||||||
|
|
@ -410,14 +412,13 @@ void audio_sample_to_uint64(const void *samples, int pos,
|
||||||
"Coreaudio and floating point samples are not supported by replay yet");
|
"Coreaudio and floating point samples are not supported by replay yet");
|
||||||
abort();
|
abort();
|
||||||
#else
|
#else
|
||||||
const struct st_sample *sample = samples;
|
|
||||||
sample += pos;
|
sample += pos;
|
||||||
*left = sample->l;
|
*left = sample->l;
|
||||||
*right = sample->r;
|
*right = sample->r;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_sample_from_uint64(void *samples, int pos,
|
void audio_sample_from_uint64(st_sample *sample, int pos,
|
||||||
uint64_t left, uint64_t right)
|
uint64_t left, uint64_t right)
|
||||||
{
|
{
|
||||||
#ifdef FLOAT_MIXENG
|
#ifdef FLOAT_MIXENG
|
||||||
|
|
@ -425,7 +426,6 @@ void audio_sample_from_uint64(void *samples, int pos,
|
||||||
"Coreaudio and floating point samples are not supported by replay yet");
|
"Coreaudio and floating point samples are not supported by replay yet");
|
||||||
abort();
|
abort();
|
||||||
#else
|
#else
|
||||||
struct st_sample *sample = samples;
|
|
||||||
sample += pos;
|
sample += pos;
|
||||||
sample->l = left;
|
sample->l = left;
|
||||||
sample->r = right;
|
sample->r = right;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ struct st_sample { mixeng_real l; mixeng_real r; };
|
||||||
struct mixeng_volume { int mute; int64_t r; int64_t l; };
|
struct mixeng_volume { int mute; int64_t r; int64_t l; };
|
||||||
struct st_sample { int64_t l; int64_t r; };
|
struct st_sample { int64_t l; int64_t r; };
|
||||||
#endif
|
#endif
|
||||||
typedef struct st_sample st_sample;
|
|
||||||
|
|
||||||
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
|
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
|
||||||
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
|
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/host-utils.h"
|
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "qemu/timer.h"
|
|
||||||
|
|
||||||
#define AUDIO_CAP "noaudio"
|
#define AUDIO_CAP "noaudio"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
@ -131,7 +129,6 @@ static struct audio_pcm_ops no_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver no_audio_driver = {
|
static struct audio_driver no_audio_driver = {
|
||||||
.name = "none",
|
.name = "none",
|
||||||
.descr = "Timer based audio emulation",
|
|
||||||
.init = no_audio_init,
|
.init = no_audio_init,
|
||||||
.fini = no_audio_fini,
|
.fini = no_audio_fini,
|
||||||
.pcm_ops = &no_pcm_ops,
|
.pcm_ops = &no_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/host-utils.h"
|
#include "qemu/host-utils.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "oss"
|
#define AUDIO_CAP "oss"
|
||||||
|
|
@ -107,13 +107,13 @@ static void oss_anal_close (int *fdp)
|
||||||
|
|
||||||
static void oss_helper_poll_out (void *opaque)
|
static void oss_helper_poll_out (void *opaque)
|
||||||
{
|
{
|
||||||
AudioState *s = opaque;
|
AudioBackend *s = opaque;
|
||||||
audio_run(s, "oss_poll_out");
|
audio_run(s, "oss_poll_out");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void oss_helper_poll_in (void *opaque)
|
static void oss_helper_poll_in (void *opaque)
|
||||||
{
|
{
|
||||||
AudioState *s = opaque;
|
AudioBackend *s = opaque;
|
||||||
audio_run(s, "oss_poll_in");
|
audio_run(s, "oss_poll_in");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ static void oss_poll_in (HWVoiceIn *hw)
|
||||||
qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
|
qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
static int aud_to_ossfmt(AudioFormat fmt, bool big_endian)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case AUDIO_FORMAT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
|
|
@ -141,18 +141,10 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
||||||
return AFMT_U8;
|
return AFMT_U8;
|
||||||
|
|
||||||
case AUDIO_FORMAT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
if (endianness) {
|
return big_endian ? AFMT_S16_BE : AFMT_S16_LE;
|
||||||
return AFMT_S16_BE;
|
|
||||||
} else {
|
|
||||||
return AFMT_S16_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AUDIO_FORMAT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
if (endianness) {
|
return big_endian ? AFMT_U16_BE : AFMT_U16_LE;
|
||||||
return AFMT_U16_BE;
|
|
||||||
} else {
|
|
||||||
return AFMT_U16_LE;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||||
|
|
@ -493,10 +485,8 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
{
|
{
|
||||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||||
struct oss_params req, obt;
|
struct oss_params req, obt;
|
||||||
int endianness;
|
|
||||||
int err;
|
int err;
|
||||||
int fd;
|
int fd;
|
||||||
AudioFormat effective_fmt;
|
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
Audiodev *dev = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
AudiodevOssOptions *oopts = &dev->u.oss;
|
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||||
|
|
@ -511,7 +501,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
err = oss_to_audfmt(obt.fmt, &obt_as.fmt, &obt_as.endianness);
|
||||||
if (err) {
|
if (err) {
|
||||||
oss_anal_close (&fd);
|
oss_anal_close (&fd);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -519,8 +509,6 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
|
|
||||||
obt_as.freq = obt.freq;
|
obt_as.freq = obt.freq;
|
||||||
obt_as.nchannels = obt.nchannels;
|
obt_as.nchannels = obt.nchannels;
|
||||||
obt_as.fmt = effective_fmt;
|
|
||||||
obt_as.endianness = endianness;
|
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
oss->nfrags = obt.nfrags;
|
oss->nfrags = obt.nfrags;
|
||||||
|
|
@ -628,10 +616,8 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
{
|
{
|
||||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||||
struct oss_params req, obt;
|
struct oss_params req, obt;
|
||||||
int endianness;
|
|
||||||
int err;
|
int err;
|
||||||
int fd;
|
int fd;
|
||||||
AudioFormat effective_fmt;
|
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
Audiodev *dev = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
|
|
@ -644,7 +630,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
err = oss_to_audfmt(obt.fmt, &obt_as.fmt, &obt_as.endianness);
|
||||||
if (err) {
|
if (err) {
|
||||||
oss_anal_close (&fd);
|
oss_anal_close (&fd);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -652,8 +638,6 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
|
|
||||||
obt_as.freq = obt.freq;
|
obt_as.freq = obt.freq;
|
||||||
obt_as.nchannels = obt.nchannels;
|
obt_as.nchannels = obt.nchannels;
|
||||||
obt_as.fmt = effective_fmt;
|
|
||||||
obt_as.endianness = endianness;
|
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
oss->nfrags = obt.nfrags;
|
oss->nfrags = obt.nfrags;
|
||||||
|
|
@ -779,7 +763,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver oss_audio_driver = {
|
static struct audio_driver oss_audio_driver = {
|
||||||
.name = "oss",
|
.name = "oss",
|
||||||
.descr = "OSS http://www.opensound.com",
|
|
||||||
.init = oss_audio_init,
|
.init = oss_audio_init,
|
||||||
.fini = oss_audio_fini,
|
.fini = oss_audio_fini,
|
||||||
.pcm_ops = &oss_pcm_ops,
|
.pcm_ops = &oss_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
@ -316,7 +316,7 @@ unlock_and_fail:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
static pa_sample_format_t audfmt_to_pa(AudioFormat afmt, bool big_endian)
|
||||||
{
|
{
|
||||||
int format;
|
int format;
|
||||||
|
|
||||||
|
|
@ -327,14 +327,14 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUDIO_FORMAT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
format = big_endian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUDIO_FORMAT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
format = big_endian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_F32:
|
case AUDIO_FORMAT_F32:
|
||||||
format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
|
format = big_endian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dolog ("Internal logic error: Bad audio format %d\n", afmt);
|
dolog ("Internal logic error: Bad audio format %d\n", afmt);
|
||||||
|
|
@ -747,14 +747,13 @@ static void qpa_volume_in(HWVoiceIn *hw, Volume *vol)
|
||||||
pa_threaded_mainloop_unlock(c->mainloop);
|
pa_threaded_mainloop_unlock(c->mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qpa_validate_per_direction_opts(Audiodev *dev,
|
static void qpa_validate_per_direction_opts(Audiodev *dev,
|
||||||
AudiodevPaPerDirectionOptions *pdo)
|
AudiodevPaPerDirectionOptions *pdo)
|
||||||
{
|
{
|
||||||
if (!pdo->has_latency) {
|
if (!pdo->has_latency) {
|
||||||
pdo->has_latency = true;
|
pdo->has_latency = true;
|
||||||
pdo->latency = 46440;
|
pdo->latency = 46440;
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* common */
|
/* common */
|
||||||
|
|
@ -844,12 +843,8 @@ static void *qpa_audio_init(Audiodev *dev, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qpa_validate_per_direction_opts(dev, popts->in)) {
|
qpa_validate_per_direction_opts(dev, popts->in);
|
||||||
return NULL;
|
qpa_validate_per_direction_opts(dev, popts->out);
|
||||||
}
|
|
||||||
if (!qpa_validate_per_direction_opts(dev, popts->out)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g = g_new0(paaudio, 1);
|
g = g_new0(paaudio, 1);
|
||||||
server = popts->server;
|
server = popts->server;
|
||||||
|
|
@ -927,7 +922,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver pa_audio_driver = {
|
static struct audio_driver pa_audio_driver = {
|
||||||
.name = "pa",
|
.name = "pa",
|
||||||
.descr = "http://www.pulseaudio.org/",
|
|
||||||
.init = qpa_audio_init,
|
.init = qpa_audio_init,
|
||||||
.fini = qpa_audio_fini,
|
.fini = qpa_audio_fini,
|
||||||
.pcm_ops = &qpa_pcm_ops,
|
.pcm_ops = &qpa_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include <spa/param/audio/format-utils.h>
|
#include <spa/param/audio/format-utils.h>
|
||||||
|
|
@ -324,7 +324,7 @@ done_unlock:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
audfmt_to_pw(AudioFormat fmt, int endianness)
|
audfmt_to_pw(AudioFormat fmt, bool big_endian)
|
||||||
{
|
{
|
||||||
int format;
|
int format;
|
||||||
|
|
||||||
|
|
@ -336,19 +336,19 @@ audfmt_to_pw(AudioFormat fmt, int endianness)
|
||||||
format = SPA_AUDIO_FORMAT_U8;
|
format = SPA_AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
format = endianness ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE;
|
format = big_endian ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
format = endianness ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE;
|
format = big_endian ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
format = endianness ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE;
|
format = big_endian ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
format = endianness ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE;
|
format = big_endian ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE;
|
||||||
break;
|
break;
|
||||||
case AUDIO_FORMAT_F32:
|
case AUDIO_FORMAT_F32:
|
||||||
format = endianness ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE;
|
format = big_endian ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dolog("Internal logic error: Bad audio format %d\n", fmt);
|
dolog("Internal logic error: Bad audio format %d\n", fmt);
|
||||||
|
|
@ -838,7 +838,6 @@ static struct audio_pcm_ops qpw_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver pw_audio_driver = {
|
static struct audio_driver pw_audio_driver = {
|
||||||
.name = "pipewire",
|
.name = "pipewire",
|
||||||
.descr = "http://www.pipewire.org/",
|
|
||||||
.init = qpw_audio_init,
|
.init = qpw_audio_init,
|
||||||
.fini = qpw_audio_fini,
|
.fini = qpw_audio_fini,
|
||||||
.pcm_ops = &qpw_pcm_ops,
|
.pcm_ops = &qpw_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
#include <SDL_thread.h>
|
#include <SDL_thread.h>
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#ifdef __sun__
|
#ifdef __sun__
|
||||||
|
|
@ -338,9 +338,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
{
|
{
|
||||||
SDLVoiceOut *sdl = (SDLVoiceOut *)hw;
|
SDLVoiceOut *sdl = (SDLVoiceOut *)hw;
|
||||||
SDL_AudioSpec req, obt;
|
SDL_AudioSpec req, obt;
|
||||||
int endianness;
|
|
||||||
int err;
|
int err;
|
||||||
AudioFormat effective_fmt;
|
|
||||||
Audiodev *dev = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out;
|
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
|
|
@ -360,7 +358,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
|
err = sdl_to_audfmt(obt.format, &obt_as.fmt, &obt_as.endianness);
|
||||||
if (err) {
|
if (err) {
|
||||||
sdl_close_out(sdl);
|
sdl_close_out(sdl);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -368,8 +366,6 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
|
|
||||||
obt_as.freq = obt.freq;
|
obt_as.freq = obt.freq;
|
||||||
obt_as.nchannels = obt.channels;
|
obt_as.nchannels = obt.channels;
|
||||||
obt_as.fmt = effective_fmt;
|
|
||||||
obt_as.endianness = endianness;
|
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
|
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
|
||||||
|
|
@ -398,9 +394,7 @@ static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
|
||||||
{
|
{
|
||||||
SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
|
SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
|
||||||
SDL_AudioSpec req, obt;
|
SDL_AudioSpec req, obt;
|
||||||
int endianness;
|
|
||||||
int err;
|
int err;
|
||||||
AudioFormat effective_fmt;
|
|
||||||
Audiodev *dev = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in;
|
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
|
|
@ -420,7 +414,7 @@ static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
|
err = sdl_to_audfmt(obt.format, &obt_as.fmt, &obt_as.endianness);
|
||||||
if (err) {
|
if (err) {
|
||||||
sdl_close_in(sdl);
|
sdl_close_in(sdl);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -428,8 +422,6 @@ static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
|
||||||
|
|
||||||
obt_as.freq = obt.freq;
|
obt_as.freq = obt.freq;
|
||||||
obt_as.nchannels = obt.channels;
|
obt_as.nchannels = obt.channels;
|
||||||
obt_as.fmt = effective_fmt;
|
|
||||||
obt_as.endianness = endianness;
|
|
||||||
|
|
||||||
audio_pcm_init_info(&hw->info, &obt_as);
|
audio_pcm_init_info(&hw->info, &obt_as);
|
||||||
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
|
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
|
||||||
|
|
@ -490,7 +482,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver sdl_audio_driver = {
|
static struct audio_driver sdl_audio_driver = {
|
||||||
.name = "sdl",
|
.name = "sdl",
|
||||||
.descr = "SDL http://www.libsdl.org",
|
|
||||||
.init = sdl_audio_init,
|
.init = sdl_audio_init,
|
||||||
.fini = sdl_audio_fini,
|
.fini = sdl_audio_fini,
|
||||||
.pcm_ops = &sdl_pcm_ops,
|
.pcm_ops = &sdl_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <sndio.h>
|
#include <sndio.h>
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "sndio"
|
#define AUDIO_CAP "sndio"
|
||||||
|
|
@ -546,7 +546,6 @@ static struct audio_pcm_ops sndio_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver sndio_audio_driver = {
|
static struct audio_driver sndio_audio_driver = {
|
||||||
.name = "sndio",
|
.name = "sndio",
|
||||||
.descr = "sndio https://sndio.org",
|
|
||||||
.init = sndio_audio_init,
|
.init = sndio_audio_init,
|
||||||
.fini = sndio_audio_fini,
|
.fini = sndio_audio_fini,
|
||||||
.pcm_ops = &sndio_pcm_ops,
|
.pcm_ops = &sndio_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#include "ui/qemu-spice.h"
|
#include "ui/qemu-spice.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "spice"
|
#define AUDIO_CAP "spice"
|
||||||
#include "audio.h"
|
#include "qemu/audio.h"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
||||||
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||||
|
|
@ -102,7 +102,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||||
#endif
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||||
settings.fmt = AUDIO_FORMAT_S16;
|
settings.fmt = AUDIO_FORMAT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = HOST_BIG_ENDIAN;
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &settings);
|
audio_pcm_init_info (&hw->info, &settings);
|
||||||
hw->samples = LINE_OUT_SAMPLES;
|
hw->samples = LINE_OUT_SAMPLES;
|
||||||
|
|
@ -218,7 +218,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
#endif
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||||
settings.fmt = AUDIO_FORMAT_S16;
|
settings.fmt = AUDIO_FORMAT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = HOST_BIG_ENDIAN;
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &settings);
|
audio_pcm_init_info (&hw->info, &settings);
|
||||||
hw->samples = LINE_IN_SAMPLES;
|
hw->samples = LINE_IN_SAMPLES;
|
||||||
|
|
@ -316,7 +316,6 @@ static struct audio_pcm_ops audio_callbacks = {
|
||||||
|
|
||||||
static struct audio_driver spice_audio_driver = {
|
static struct audio_driver spice_audio_driver = {
|
||||||
.name = "spice",
|
.name = "spice",
|
||||||
.descr = "spice audio driver",
|
|
||||||
.init = spice_audio_init,
|
.init = spice_audio_init,
|
||||||
.fini = spice_audio_fini,
|
.fini = spice_audio_fini,
|
||||||
.pcm_ops = &audio_callbacks,
|
.pcm_ops = &audio_callbacks,
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/host-utils.h"
|
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/audio.h"
|
||||||
#include "qapi/opts-visitor.h"
|
|
||||||
#include "audio.h"
|
|
||||||
|
|
||||||
#define AUDIO_CAP "wav"
|
#define AUDIO_CAP "wav"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
@ -208,7 +205,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
|
||||||
|
|
||||||
static struct audio_driver wav_audio_driver = {
|
static struct audio_driver wav_audio_driver = {
|
||||||
.name = "wav",
|
.name = "wav",
|
||||||
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
|
|
||||||
.init = wav_audio_init,
|
.init = wav_audio_init,
|
||||||
.fini = wav_audio_fini,
|
.fini = wav_audio_fini,
|
||||||
.pcm_ops = &wav_pcm_ops,
|
.pcm_ops = &wav_pcm_ops,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/qemu-print.h"
|
#include "qemu/qemu-print.h"
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "audio.h"
|
#include "audio_int.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
@ -104,7 +103,7 @@ static struct capture_ops wav_capture_ops = {
|
||||||
.info = wav_capture_info
|
.info = wav_capture_info
|
||||||
};
|
};
|
||||||
|
|
||||||
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
|
int wav_start_capture(AudioBackend *state, CaptureState *s, const char *path,
|
||||||
int freq, int bits, int nchannels)
|
int freq, int bits, int nchannels)
|
||||||
{
|
{
|
||||||
WAVState *wav;
|
WAVState *wav;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ typedef struct CryptoDevBackendBuiltinSession {
|
||||||
|
|
||||||
#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
|
#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
|
||||||
#define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN 64
|
#define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN 64
|
||||||
|
/* demonstration purposes only, use a limited size to avoid QEMU OOM */
|
||||||
|
#define CRYPTODEV_BUITLIN_MAX_REQUEST_SIZE (1024 * 1024)
|
||||||
|
|
||||||
struct CryptoDevBackendBuiltin {
|
struct CryptoDevBackendBuiltin {
|
||||||
CryptoDevBackend parent_obj;
|
CryptoDevBackend parent_obj;
|
||||||
|
|
@ -98,12 +100,7 @@ static void cryptodev_builtin_init(
|
||||||
1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC;
|
1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC;
|
||||||
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
|
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
|
||||||
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
|
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
|
||||||
/*
|
backend->conf.max_size = CRYPTODEV_BUITLIN_MAX_REQUEST_SIZE;
|
||||||
* Set the Maximum length of crypto request.
|
|
||||||
* Why this value? Just avoid to overflow when
|
|
||||||
* memory allocation for each crypto request.
|
|
||||||
*/
|
|
||||||
backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
|
|
||||||
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
|
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
|
||||||
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
|
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
|
||||||
cryptodev_builtin_init_akcipher(backend);
|
cryptodev_builtin_init_akcipher(backend);
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ typedef struct CryptoDevBackendLKCFSession {
|
||||||
QCryptoAkCipherOptions akcipher_opts;
|
QCryptoAkCipherOptions akcipher_opts;
|
||||||
} CryptoDevBackendLKCFSession;
|
} CryptoDevBackendLKCFSession;
|
||||||
|
|
||||||
typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF;
|
|
||||||
typedef struct CryptoDevLKCFTask CryptoDevLKCFTask;
|
typedef struct CryptoDevLKCFTask CryptoDevLKCFTask;
|
||||||
struct CryptoDevLKCFTask {
|
struct CryptoDevLKCFTask {
|
||||||
CryptoDevBackendLKCFSession *sess;
|
CryptoDevBackendLKCFSession *sess;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ struct CryptoDevBackendVhostUser {
|
||||||
CryptoDevBackend parent_obj;
|
CryptoDevBackend parent_obj;
|
||||||
|
|
||||||
VhostUserState vhost_user;
|
VhostUserState vhost_user;
|
||||||
CharBackend chr;
|
CharFrontend chr;
|
||||||
char *chr_name;
|
char *chr_name;
|
||||||
bool opened;
|
bool opened;
|
||||||
CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM];
|
CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM];
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ have_fd:
|
||||||
/* Let's do the same as memory-backend-ram,share=on would do. */
|
/* Let's do the same as memory-backend-ram,share=on would do. */
|
||||||
ram_flags = RAM_SHARED;
|
ram_flags = RAM_SHARED;
|
||||||
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
|
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
|
||||||
|
ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0;
|
||||||
|
|
||||||
return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
||||||
backend_name, backend->size,
|
backend_name, backend->size,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
#include "system/igvm-cfg.h"
|
#include "system/igvm-cfg.h"
|
||||||
#include "igvm.h"
|
#include "system/igvm.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
|
|
||||||
static char *get_igvm(Object *obj, Error **errp)
|
static char *get_igvm(Object *obj, Error **errp)
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
#include "igvm.h"
|
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/target-info-qapi.h"
|
||||||
|
#include "system/igvm.h"
|
||||||
#include "system/memory.h"
|
#include "system/memory.h"
|
||||||
#include "system/address-spaces.h"
|
#include "system/address-spaces.h"
|
||||||
#include "hw/core/cpu.h"
|
#include "hw/core/cpu.h"
|
||||||
|
|
@ -431,18 +432,6 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* A confidential guest support object must be provided for setting
|
|
||||||
* a VP context.
|
|
||||||
*/
|
|
||||||
if (!ctx->cgs) {
|
|
||||||
error_setg(
|
|
||||||
errp,
|
|
||||||
"A VP context is present in the IGVM file but is not supported "
|
|
||||||
"by the current system.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
|
data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||||
ctx->current_header_index);
|
ctx->current_header_index);
|
||||||
if (data_handle < 0) {
|
if (data_handle < 0) {
|
||||||
|
|
@ -452,9 +441,21 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
|
data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
|
||||||
|
|
||||||
|
if (ctx->cgs) {
|
||||||
result = ctx->cgsc->set_guest_state(
|
result = ctx->cgsc->set_guest_state(
|
||||||
vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
|
vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
|
||||||
CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
|
CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
|
||||||
|
} else if (target_arch() == SYS_EMU_TARGET_X86_64) {
|
||||||
|
result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp);
|
||||||
|
} else {
|
||||||
|
error_setg(
|
||||||
|
errp,
|
||||||
|
"A VP context is present in the IGVM file but is not supported "
|
||||||
|
"by the current system.");
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
|
||||||
igvm_free_buffer(ctx->file, data_handle);
|
igvm_free_buffer(ctx->file, data_handle);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -543,6 +544,8 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
|
const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
|
||||||
|
int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry,
|
||||||
|
Error **errp) = NULL;
|
||||||
QIgvmParameterData *param_entry;
|
QIgvmParameterData *param_entry;
|
||||||
int max_entry_count;
|
int max_entry_count;
|
||||||
int entry = 0;
|
int entry = 0;
|
||||||
|
|
@ -550,7 +553,13 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
|
||||||
ConfidentialGuestMemoryMapEntry cgmm_entry;
|
ConfidentialGuestMemoryMapEntry cgmm_entry;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
if (!ctx->cgs) {
|
if (ctx->cgs && ctx->cgsc->get_mem_map_entry) {
|
||||||
|
get_mem_map_entry = ctx->cgsc->get_mem_map_entry;
|
||||||
|
|
||||||
|
} else if (target_arch() == SYS_EMU_TARGET_X86_64) {
|
||||||
|
get_mem_map_entry = qigvm_x86_get_mem_map_entry;
|
||||||
|
|
||||||
|
} else {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"IGVM file contains a memory map but this is not supported "
|
"IGVM file contains a memory map but this is not supported "
|
||||||
"by the current system.");
|
"by the current system.");
|
||||||
|
|
@ -565,9 +574,9 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
|
||||||
param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
|
param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
|
||||||
mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
|
mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
|
||||||
|
|
||||||
retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp);
|
retval = get_mem_map_entry(entry, &cgmm_entry, errp);
|
||||||
while (retval == 0) {
|
while (retval == 0) {
|
||||||
if (entry > max_entry_count) {
|
if (entry >= max_entry_count) {
|
||||||
error_setg(
|
error_setg(
|
||||||
errp,
|
errp,
|
||||||
"IGVM: guest memory map size exceeds parameter area defined in IGVM file");
|
"IGVM: guest memory map size exceeds parameter area defined in IGVM file");
|
||||||
|
|
@ -598,8 +607,7 @@ static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
|
||||||
IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
|
IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
retval =
|
retval = get_mem_map_entry(++entry, &cgmm_entry, errp);
|
||||||
ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
|
|
||||||
}
|
}
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id)
|
||||||
}
|
}
|
||||||
|
|
||||||
int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
|
int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
|
||||||
ram_addr_t size, void *vaddr, bool readonly)
|
uint64_t size, void *vaddr, bool readonly)
|
||||||
{
|
{
|
||||||
int ret, fd = be->fd;
|
int ret, fd = be->fd;
|
||||||
struct iommu_ioas_map map = {
|
struct iommu_ioas_map map = {
|
||||||
|
|
@ -230,7 +230,7 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
|
||||||
}
|
}
|
||||||
|
|
||||||
int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||||
hwaddr iova, ram_addr_t size,
|
hwaddr iova, uint64_t size,
|
||||||
int mfd, unsigned long start, bool readonly)
|
int mfd, unsigned long start, bool readonly)
|
||||||
{
|
{
|
||||||
int ret, fd = be->fd;
|
int ret, fd = be->fd;
|
||||||
|
|
@ -268,7 +268,7 @@ int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||||
hwaddr iova, ram_addr_t size)
|
hwaddr iova, uint64_t size)
|
||||||
{
|
{
|
||||||
int ret, fd = be->fd;
|
int ret, fd = be->fd;
|
||||||
struct iommu_ioas_unmap unmap = {
|
struct iommu_ioas_unmap unmap = {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(RngEgd, RNG_EGD)
|
||||||
struct RngEgd {
|
struct RngEgd {
|
||||||
RngBackend parent;
|
RngBackend parent;
|
||||||
|
|
||||||
CharBackend chr;
|
CharFrontend chr;
|
||||||
char *chr_name;
|
char *chr_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "system/spdm-socket.h"
|
#include "system/spdm-socket.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "hw/qdev-properties-system.h"
|
||||||
|
#include "hw/core/qdev-prop-internal.h"
|
||||||
|
|
||||||
static bool read_bytes(const int socket, uint8_t *buffer,
|
static bool read_bytes(const int socket, uint8_t *buffer,
|
||||||
size_t number_of_bytes)
|
size_t number_of_bytes)
|
||||||
|
|
@ -184,29 +187,61 @@ int spdm_socket_connect(uint16_t port, Error **errp)
|
||||||
return client_socket;
|
return client_socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
|
static bool spdm_socket_command_valid(uint32_t command)
|
||||||
void *req, uint32_t req_len,
|
{
|
||||||
|
switch (command) {
|
||||||
|
case SPDM_SOCKET_COMMAND_NORMAL:
|
||||||
|
case SPDM_SOCKET_STORAGE_CMD_IF_SEND:
|
||||||
|
case SPDM_SOCKET_STORAGE_CMD_IF_RECV:
|
||||||
|
case SOCKET_SPDM_STORAGE_ACK_STATUS:
|
||||||
|
case SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE:
|
||||||
|
case SPDM_SOCKET_COMMAND_CONTINUE:
|
||||||
|
case SPDM_SOCKET_COMMAND_SHUTDOWN:
|
||||||
|
case SPDM_SOCKET_COMMAND_UNKOWN:
|
||||||
|
case SPDM_SOCKET_COMMAND_TEST:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
|
||||||
void *rsp, uint32_t rsp_len)
|
void *rsp, uint32_t rsp_len)
|
||||||
{
|
{
|
||||||
uint32_t command;
|
uint32_t command;
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
result = send_platform_data(socket, transport_type,
|
|
||||||
SPDM_SOCKET_COMMAND_NORMAL,
|
|
||||||
req, req_len);
|
|
||||||
if (!result) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = receive_platform_data(socket, transport_type, &command,
|
result = receive_platform_data(socket, transport_type, &command,
|
||||||
(uint8_t *)rsp, &rsp_len);
|
(uint8_t *)rsp, &rsp_len);
|
||||||
|
|
||||||
|
/* we may have received some data, but check if the command is valid */
|
||||||
|
if (!result || !spdm_socket_command_valid(command)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsp_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool spdm_socket_send(const int socket, uint32_t socket_cmd,
|
||||||
|
uint32_t transport_type, void *req, uint32_t req_len)
|
||||||
|
{
|
||||||
|
return send_platform_data(socket, transport_type, socket_cmd, req,
|
||||||
|
req_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
|
||||||
|
void *req, uint32_t req_len,
|
||||||
|
void *rsp, uint32_t rsp_len)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
result = spdm_socket_send(socket, SPDM_SOCKET_COMMAND_NORMAL,
|
||||||
|
transport_type, req, req_len);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(command != 0);
|
return spdm_socket_receive(socket, transport_type, rsp, rsp_len);
|
||||||
|
|
||||||
return rsp_len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spdm_socket_close(const int socket, uint32_t transport_type)
|
void spdm_socket_close(const int socket, uint32_t transport_type)
|
||||||
|
|
@ -214,3 +249,23 @@ void spdm_socket_close(const int socket, uint32_t transport_type)
|
||||||
send_platform_data(socket, transport_type,
|
send_platform_data(socket, transport_type,
|
||||||
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
|
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QEnumLookup SpdmTransport_lookup = {
|
||||||
|
.array = (const char *const[]) {
|
||||||
|
[SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC] = "unspecified",
|
||||||
|
[SPDM_SOCKET_TRANSPORT_TYPE_MCTP] = "mctp",
|
||||||
|
[SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE] = "doe",
|
||||||
|
[SPDM_SOCKET_TRANSPORT_TYPE_SCSI] = "scsi",
|
||||||
|
[SPDM_SOCKET_TRANSPORT_TYPE_NVME] = "nvme",
|
||||||
|
},
|
||||||
|
.size = SPDM_SOCKET_TRANSPORT_TYPE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
const PropertyInfo qdev_prop_spdm_trans = {
|
||||||
|
.type = "SpdmTransportType",
|
||||||
|
.description = "Spdm Transport, doe/nvme/mctp/scsi/unspecified",
|
||||||
|
.enum_table = &SpdmTransport_lookup,
|
||||||
|
.get = qdev_propinfo_get_enum,
|
||||||
|
.set = qdev_propinfo_set_enum,
|
||||||
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ struct TPMEmulator {
|
||||||
TPMBackend parent;
|
TPMBackend parent;
|
||||||
|
|
||||||
TPMEmulatorOptions *options;
|
TPMEmulatorOptions *options;
|
||||||
CharBackend ctrl_chr;
|
CharFrontend ctrl_chr;
|
||||||
QIOChannel *data_ioc;
|
QIOChannel *data_ioc;
|
||||||
TPMVersion tpm_version;
|
TPMVersion tpm_version;
|
||||||
uint32_t caps; /* capabilities of the TPM */
|
uint32_t caps; /* capabilities of the TPM */
|
||||||
|
|
@ -126,7 +126,7 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
|
||||||
size_t msg_len_in, size_t msg_len_out_err,
|
size_t msg_len_in, size_t msg_len_out_err,
|
||||||
size_t msg_len_out_total)
|
size_t msg_len_out_total)
|
||||||
{
|
{
|
||||||
CharBackend *dev = &tpm->ctrl_chr;
|
CharFrontend *dev = &tpm->ctrl_chr;
|
||||||
uint32_t cmd_no = cpu_to_be32(cmd);
|
uint32_t cmd_no = cpu_to_be32(cmd);
|
||||||
ssize_t n = sizeof(uint32_t) + msg_len_in;
|
ssize_t n = sizeof(uint32_t) + msg_len_in;
|
||||||
ptm_res res;
|
ptm_res res;
|
||||||
|
|
@ -308,21 +308,21 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tpm_emulator_stop_tpm(TPMBackend *tb)
|
static int tpm_emulator_stop_tpm(TPMBackend *tb, Error **errp)
|
||||||
{
|
{
|
||||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||||
ptm_res res;
|
ptm_res res;
|
||||||
|
|
||||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0,
|
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0,
|
||||||
sizeof(ptm_res), sizeof(res)) < 0) {
|
sizeof(ptm_res), sizeof(res)) < 0) {
|
||||||
error_report("tpm-emulator: Could not stop TPM: %s",
|
error_setg(errp, "tpm-emulator: Could not stop TPM: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = be32_to_cpu(res);
|
res = be32_to_cpu(res);
|
||||||
if (res) {
|
if (res) {
|
||||||
error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res,
|
error_setg(errp, "tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res,
|
||||||
tpm_emulator_strerror(res));
|
tpm_emulator_strerror(res));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -362,12 +362,13 @@ static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu)
|
||||||
|
|
||||||
static int tpm_emulator_set_buffer_size(TPMBackend *tb,
|
static int tpm_emulator_set_buffer_size(TPMBackend *tb,
|
||||||
size_t wanted_size,
|
size_t wanted_size,
|
||||||
size_t *actual_size)
|
size_t *actual_size,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||||
ptm_setbuffersize psbs;
|
ptm_setbuffersize psbs;
|
||||||
|
|
||||||
if (tpm_emulator_stop_tpm(tb) < 0) {
|
if (tpm_emulator_stop_tpm(tb, errp) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,14 +377,15 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
|
||||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
|
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
|
||||||
sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result),
|
sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result),
|
||||||
sizeof(psbs.u.resp)) < 0) {
|
sizeof(psbs.u.resp)) < 0) {
|
||||||
error_report("tpm-emulator: Could not set buffer size: %s",
|
error_setg(errp, "tpm-emulator: Could not set buffer size: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
|
psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
|
||||||
if (psbs.u.resp.tpm_result != 0) {
|
if (psbs.u.resp.tpm_result != 0) {
|
||||||
error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s",
|
error_setg(errp,
|
||||||
|
"tpm-emulator: TPM result for set buffer size : 0x%x %s",
|
||||||
psbs.u.resp.tpm_result,
|
psbs.u.resp.tpm_result,
|
||||||
tpm_emulator_strerror(psbs.u.resp.tpm_result));
|
tpm_emulator_strerror(psbs.u.resp.tpm_result));
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -402,7 +404,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
|
static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
|
||||||
bool is_resume)
|
bool is_resume, Error **errp)
|
||||||
{
|
{
|
||||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||||
ptm_init init = {
|
ptm_init init = {
|
||||||
|
|
@ -413,7 +415,7 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
|
||||||
trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize);
|
trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize);
|
||||||
|
|
||||||
if (buffersize != 0 &&
|
if (buffersize != 0 &&
|
||||||
tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) {
|
tpm_emulator_set_buffer_size(tb, buffersize, NULL, errp) < 0) {
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,14 +426,14 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
|
||||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
|
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
|
||||||
sizeof(init.u.resp.tpm_result),
|
sizeof(init.u.resp.tpm_result),
|
||||||
sizeof(init)) < 0) {
|
sizeof(init)) < 0) {
|
||||||
error_report("tpm-emulator: could not send INIT: %s",
|
error_setg(errp, "tpm-emulator: could not send INIT: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = be32_to_cpu(init.u.resp.tpm_result);
|
res = be32_to_cpu(init.u.resp.tpm_result);
|
||||||
if (res) {
|
if (res) {
|
||||||
error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res,
|
error_setg(errp, "tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res,
|
||||||
tpm_emulator_strerror(res));
|
tpm_emulator_strerror(res));
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
}
|
}
|
||||||
|
|
@ -441,18 +443,31 @@ err_exit:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
static int do_tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* TPM startup will be done from post_load hook */
|
/* TPM startup will be done from post_load hook */
|
||||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||||
if (buffersize != 0) {
|
if (buffersize != 0) {
|
||||||
return tpm_emulator_set_buffer_size(tb, buffersize, NULL);
|
return tpm_emulator_set_buffer_size(tb, buffersize, NULL, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tpm_emulator_startup_tpm_resume(tb, buffersize, false);
|
return tpm_emulator_startup_tpm_resume(tb, buffersize, false, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int ret = do_tpm_emulator_startup_tpm(tb, buffersize, &local_err);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
|
static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
|
||||||
|
|
@ -546,7 +561,7 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
|
||||||
{
|
{
|
||||||
size_t actual_size;
|
size_t actual_size;
|
||||||
|
|
||||||
if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) {
|
if (tpm_emulator_set_buffer_size(tb, 0, &actual_size, NULL) < 0) {
|
||||||
return 4096;
|
return 4096;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -819,7 +834,8 @@ static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu)
|
||||||
static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
|
static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
|
||||||
uint32_t type,
|
uint32_t type,
|
||||||
TPMSizedBuffer *tsb,
|
TPMSizedBuffer *tsb,
|
||||||
uint32_t flags)
|
uint32_t flags,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
ptm_setstate pss;
|
ptm_setstate pss;
|
||||||
|
|
@ -838,15 +854,16 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
|
||||||
/* write the header only */
|
/* write the header only */
|
||||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
|
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
|
||||||
offsetof(ptm_setstate, u.req.data), 0, 0) < 0) {
|
offsetof(ptm_setstate, u.req.data), 0, 0) < 0) {
|
||||||
error_report("tpm-emulator: could not set state blob type %d : %s",
|
error_setg_errno(errp, errno,
|
||||||
type, strerror(errno));
|
"tpm-emulator: could not set state blob type %d",
|
||||||
|
type);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now the body */
|
/* now the body */
|
||||||
n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size);
|
n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size);
|
||||||
if (n != tsb->size) {
|
if (n != tsb->size) {
|
||||||
error_report("tpm-emulator: Writing the stateblob (type %d) "
|
error_setg(errp, "tpm-emulator: Writing the stateblob (type %d) "
|
||||||
"failed; could not write %u bytes, but only %zd",
|
"failed; could not write %u bytes, but only %zd",
|
||||||
type, tsb->size, n);
|
type, tsb->size, n);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -856,16 +873,16 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
|
||||||
n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr,
|
n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr,
|
||||||
(uint8_t *)&pss, sizeof(pss.u.resp));
|
(uint8_t *)&pss, sizeof(pss.u.resp));
|
||||||
if (n != sizeof(pss.u.resp)) {
|
if (n != sizeof(pss.u.resp)) {
|
||||||
error_report("tpm-emulator: Reading response from writing stateblob "
|
error_setg(errp, "tpm-emulator: Reading response from writing "
|
||||||
"(type %d) failed; expected %zu bytes, got %zd", type,
|
"stateblob (type %d) failed; expected %zu bytes, "
|
||||||
sizeof(pss.u.resp), n);
|
"got %zd", type, sizeof(pss.u.resp), n);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
|
tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
|
||||||
if (tpm_result != 0) {
|
if (tpm_result != 0) {
|
||||||
error_report("tpm-emulator: Setting the stateblob (type %d) failed "
|
error_setg(errp, "tpm-emulator: Setting the stateblob (type %d) "
|
||||||
"with a TPM error 0x%x %s", type, tpm_result,
|
"failed with a TPM error 0x%x %s", type, tpm_result,
|
||||||
tpm_emulator_strerror(tpm_result));
|
tpm_emulator_strerror(tpm_result));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -880,27 +897,27 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
|
||||||
*
|
*
|
||||||
* Returns a negative errno code in case of error.
|
* Returns a negative errno code in case of error.
|
||||||
*/
|
*/
|
||||||
static int tpm_emulator_set_state_blobs(TPMBackend *tb)
|
static int tpm_emulator_set_state_blobs(TPMBackend *tb, Error **errp)
|
||||||
{
|
{
|
||||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||||
TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
|
TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
|
||||||
|
|
||||||
trace_tpm_emulator_set_state_blobs();
|
trace_tpm_emulator_set_state_blobs();
|
||||||
|
|
||||||
if (tpm_emulator_stop_tpm(tb) < 0) {
|
if (tpm_emulator_stop_tpm(tb, errp) < 0) {
|
||||||
trace_tpm_emulator_set_state_blobs_error("Could not stop TPM");
|
trace_tpm_emulator_set_state_blobs_error("Could not stop TPM");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
|
if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
|
||||||
&state_blobs->permanent,
|
&state_blobs->permanent,
|
||||||
state_blobs->permanent_flags) < 0 ||
|
state_blobs->permanent_flags, errp) < 0 ||
|
||||||
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
|
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
|
||||||
&state_blobs->volatil,
|
&state_blobs->volatil,
|
||||||
state_blobs->volatil_flags) < 0 ||
|
state_blobs->volatil_flags, errp) < 0 ||
|
||||||
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
|
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
|
||||||
&state_blobs->savestate,
|
&state_blobs->savestate,
|
||||||
state_blobs->savestate_flags) < 0) {
|
state_blobs->savestate_flags, errp) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -945,31 +962,29 @@ static void tpm_emulator_vm_state_change(void *opaque, bool running,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load the TPM state blobs into the TPM.
|
* Load the TPM state blobs into the TPM.
|
||||||
*
|
|
||||||
* Returns negative errno codes in case of error.
|
|
||||||
*/
|
*/
|
||||||
static int tpm_emulator_post_load(void *opaque, int version_id)
|
static bool tpm_emulator_post_load(void *opaque, int version_id, Error **errp)
|
||||||
{
|
{
|
||||||
TPMBackend *tb = opaque;
|
TPMBackend *tb = opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = tpm_emulator_set_state_blobs(tb);
|
ret = tpm_emulator_set_state_blobs(tb, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) {
|
if (tpm_emulator_startup_tpm_resume(tb, 0, true, errp) < 0) {
|
||||||
return -EIO;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_tpm_emulator = {
|
static const VMStateDescription vmstate_tpm_emulator = {
|
||||||
.name = "tpm-emulator",
|
.name = "tpm-emulator",
|
||||||
.version_id = 0,
|
.version_id = 0,
|
||||||
.pre_save = tpm_emulator_pre_save,
|
.pre_save = tpm_emulator_pre_save,
|
||||||
.post_load = tpm_emulator_post_load,
|
.post_load_errp = tpm_emulator_post_load,
|
||||||
.fields = (const VMStateField[]) {
|
.fields = (const VMStateField[]) {
|
||||||
VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator),
|
VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator),
|
||||||
VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator),
|
VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator),
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb)
|
||||||
static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
|
static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
char *dev;
|
const char *dev;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
||||||
if (tpm_pt->options->cancel_path) {
|
if (tpm_pt->options->cancel_path) {
|
||||||
|
|
|
||||||
57
block.c
57
block.c
|
|
@ -606,12 +606,13 @@ create_file_fallback_zero_first_sector(BlockBackend *blk,
|
||||||
int64_t current_size,
|
int64_t current_size,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
uint32_t alignment = blk_get_pwrite_zeroes_alignment(blk);
|
||||||
int64_t bytes_to_clear;
|
int64_t bytes_to_clear;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
|
|
||||||
bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE);
|
bytes_to_clear = MIN(current_size, MAX(BDRV_SECTOR_SIZE, alignment));
|
||||||
if (bytes_to_clear) {
|
if (bytes_to_clear) {
|
||||||
ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
|
ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
@ -693,7 +694,7 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
|
int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
|
||||||
Error **errp)
|
bool allow_protocol_prefix, Error **errp)
|
||||||
{
|
{
|
||||||
QemuOpts *protocol_opts;
|
QemuOpts *protocol_opts;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
|
|
@ -702,7 +703,7 @@ int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
|
||||||
|
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
|
|
||||||
drv = bdrv_find_protocol(filename, true, errp);
|
drv = bdrv_find_protocol(filename, allow_protocol_prefix, errp);
|
||||||
if (drv == NULL) {
|
if (drv == NULL) {
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
@ -1497,6 +1498,17 @@ static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void coroutine_fn GRAPH_RDLOCK bdrv_child_cb_resize(BdrvChild *child)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = child->opaque;
|
||||||
|
|
||||||
|
if (child->role & BDRV_CHILD_FILTERED) {
|
||||||
|
/* Best effort, ignore errors. */
|
||||||
|
bdrv_co_refresh_total_sectors(bs, bs->total_sectors);
|
||||||
|
bdrv_co_parent_cb_resize(bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
|
static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
bool backing_mask_protocol,
|
bool backing_mask_protocol,
|
||||||
|
|
@ -1529,6 +1541,7 @@ const BdrvChildClass child_of_bds = {
|
||||||
.detach = bdrv_child_cb_detach,
|
.detach = bdrv_child_cb_detach,
|
||||||
.inactivate = bdrv_child_cb_inactivate,
|
.inactivate = bdrv_child_cb_inactivate,
|
||||||
.change_aio_ctx = bdrv_child_cb_change_aio_ctx,
|
.change_aio_ctx = bdrv_child_cb_change_aio_ctx,
|
||||||
|
.resize = bdrv_child_cb_resize,
|
||||||
.update_filename = bdrv_child_cb_update_filename,
|
.update_filename = bdrv_child_cb_update_filename,
|
||||||
.get_parent_aio_context = child_of_bds_get_parent_aio_context,
|
.get_parent_aio_context = child_of_bds_get_parent_aio_context,
|
||||||
};
|
};
|
||||||
|
|
@ -5386,17 +5399,13 @@ bdrv_replace_node_noperm(BlockDriverState *from,
|
||||||
*
|
*
|
||||||
* With auto_skip=false the error is returned if from has a parent which should
|
* With auto_skip=false the error is returned if from has a parent which should
|
||||||
* not be updated.
|
* not be updated.
|
||||||
*
|
|
||||||
* With @detach_subchain=true @to must be in a backing chain of @from. In this
|
|
||||||
* case backing link of the cow-parent of @to is removed.
|
|
||||||
*/
|
*/
|
||||||
static int GRAPH_WRLOCK
|
static int GRAPH_WRLOCK
|
||||||
bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
|
bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
|
||||||
bool auto_skip, bool detach_subchain, Error **errp)
|
bool auto_skip, Error **errp)
|
||||||
{
|
{
|
||||||
Transaction *tran = tran_new();
|
Transaction *tran = tran_new();
|
||||||
g_autoptr(GSList) refresh_list = NULL;
|
g_autoptr(GSList) refresh_list = NULL;
|
||||||
BlockDriverState *to_cow_parent = NULL;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
|
|
@ -5405,17 +5414,6 @@ bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
|
||||||
assert(to->quiesce_counter);
|
assert(to->quiesce_counter);
|
||||||
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
|
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
|
||||||
|
|
||||||
if (detach_subchain) {
|
|
||||||
assert(bdrv_chain_contains(from, to));
|
|
||||||
assert(from != to);
|
|
||||||
for (to_cow_parent = from;
|
|
||||||
bdrv_filter_or_cow_bs(to_cow_parent) != to;
|
|
||||||
to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent))
|
|
||||||
{
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do the replacement without permission update.
|
* Do the replacement without permission update.
|
||||||
* Replacement may influence the permissions, we should calculate new
|
* Replacement may influence the permissions, we should calculate new
|
||||||
|
|
@ -5427,11 +5425,6 @@ bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detach_subchain) {
|
|
||||||
/* to_cow_parent is already drained because from is drained */
|
|
||||||
bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_list = g_slist_prepend(refresh_list, to);
|
refresh_list = g_slist_prepend(refresh_list, to);
|
||||||
refresh_list = g_slist_prepend(refresh_list, from);
|
refresh_list = g_slist_prepend(refresh_list, from);
|
||||||
|
|
||||||
|
|
@ -5450,7 +5443,7 @@ out:
|
||||||
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
return bdrv_replace_node_common(from, to, true, false, errp);
|
return bdrv_replace_node_common(from, to, true, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
|
int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
|
||||||
|
|
@ -5466,7 +5459,7 @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
|
||||||
|
|
||||||
bdrv_drained_begin(child_bs);
|
bdrv_drained_begin(child_bs);
|
||||||
bdrv_graph_wrlock();
|
bdrv_graph_wrlock();
|
||||||
ret = bdrv_replace_node_common(bs, child_bs, true, true, errp);
|
ret = bdrv_replace_node_common(bs, child_bs, true, errp);
|
||||||
bdrv_graph_wrunlock();
|
bdrv_graph_wrunlock();
|
||||||
bdrv_drained_end(child_bs);
|
bdrv_drained_end(child_bs);
|
||||||
|
|
||||||
|
|
@ -5917,17 +5910,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||||
updated_children = g_slist_prepend(updated_children, c);
|
updated_children = g_slist_prepend(updated_children, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
bdrv_replace_node_common(top, base, false, &local_err);
|
||||||
* It seems correct to pass detach_subchain=true here, but it triggers
|
|
||||||
* one more yet not fixed bug, when due to nested aio_poll loop we switch to
|
|
||||||
* another drained section, which modify the graph (for example, removing
|
|
||||||
* the child, which we keep in updated_children list). So, it's a TODO.
|
|
||||||
*
|
|
||||||
* Note, bug triggered if pass detach_subchain=true here and run
|
|
||||||
* test-bdrv-drain. test_drop_intermediate_poll() test-case will crash.
|
|
||||||
* That's a FIXME.
|
|
||||||
*/
|
|
||||||
bdrv_replace_node_common(top, base, false, false, &local_err);
|
|
||||||
bdrv_graph_wrunlock();
|
bdrv_graph_wrunlock();
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "system/qtest.h"
|
#include "system/qtest.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
|
||||||
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
|
||||||
|
|
@ -56,13 +57,25 @@ static bool bool_from_onoffauto(OnOffAuto val, bool def)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
|
bool block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
|
||||||
enum OnOffAuto account_failed)
|
enum OnOffAuto account_failed, uint32_t *stats_intervals,
|
||||||
|
uint32_t num_stats_intervals, Error **errp)
|
||||||
{
|
{
|
||||||
stats->account_invalid = bool_from_onoffauto(account_invalid,
|
stats->account_invalid = bool_from_onoffauto(account_invalid,
|
||||||
stats->account_invalid);
|
stats->account_invalid);
|
||||||
stats->account_failed = bool_from_onoffauto(account_failed,
|
stats->account_failed = bool_from_onoffauto(account_failed,
|
||||||
stats->account_failed);
|
stats->account_failed);
|
||||||
|
if (stats_intervals) {
|
||||||
|
for (int i = 0; i < num_stats_intervals; i++) {
|
||||||
|
if (stats_intervals[i] <= 0) {
|
||||||
|
error_setg(errp, "Invalid interval length: %u", stats_intervals[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
block_acct_add_interval(stats, stats_intervals[i]);
|
||||||
|
}
|
||||||
|
g_free(stats_intervals);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_acct_cleanup(BlockAcctStats *stats)
|
void block_acct_cleanup(BlockAcctStats *stats)
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,10 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs,
|
||||||
Coroutine *co)
|
Coroutine *co)
|
||||||
{
|
{
|
||||||
Request *req = g_new(Request, 1);
|
Request *req = g_new(Request, 1);
|
||||||
|
AioContext *ctx = qemu_coroutine_get_aio_context(co);
|
||||||
*req = (Request) {
|
*req = (Request) {
|
||||||
.co = co,
|
.co = co,
|
||||||
.bh = aio_bh_new(bdrv_get_aio_context(bs), blkreplay_bh_cb, req),
|
.bh = aio_bh_new(ctx, blkreplay_bh_cb, req),
|
||||||
};
|
};
|
||||||
replay_block_event(req->bh, reqid);
|
replay_block_event(req->bh, reqid);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1318,9 +1318,9 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
|
||||||
* section.
|
* section.
|
||||||
*/
|
*/
|
||||||
qemu_mutex_lock(&blk->queued_requests_lock);
|
qemu_mutex_lock(&blk->queued_requests_lock);
|
||||||
|
/* blk_root_drained_end() has the corresponding blk_inc_in_flight() */
|
||||||
blk_dec_in_flight(blk);
|
blk_dec_in_flight(blk);
|
||||||
qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock);
|
qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock);
|
||||||
blk_inc_in_flight(blk);
|
|
||||||
qemu_mutex_unlock(&blk->queued_requests_lock);
|
qemu_mutex_unlock(&blk->queued_requests_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2305,6 +2305,17 @@ uint32_t blk_get_request_alignment(BlockBackend *blk)
|
||||||
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
|
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the optimal write zeroes alignment, in bytes; guaranteed nonzero */
|
||||||
|
uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
IO_CODE();
|
||||||
|
if (!bs) {
|
||||||
|
return BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
return bs->bl.pwrite_zeroes_alignment ?: bs->bl.request_alignment;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
|
/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
|
||||||
uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
|
uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
|
|
@ -2767,9 +2778,11 @@ static void blk_root_drained_end(BdrvChild *child)
|
||||||
blk->dev_ops->drained_end(blk->dev_opaque);
|
blk->dev_ops->drained_end(blk->dev_opaque);
|
||||||
}
|
}
|
||||||
qemu_mutex_lock(&blk->queued_requests_lock);
|
qemu_mutex_lock(&blk->queued_requests_lock);
|
||||||
while (qemu_co_enter_next(&blk->queued_requests,
|
while (!qemu_co_queue_empty(&blk->queued_requests)) {
|
||||||
&blk->queued_requests_lock)) {
|
|
||||||
/* Resume all queued requests */
|
/* Resume all queued requests */
|
||||||
|
blk_inc_in_flight(blk);
|
||||||
|
qemu_co_enter_next(&blk->queued_requests,
|
||||||
|
&blk->queued_requests_lock);
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(&blk->queued_requests_lock);
|
qemu_mutex_unlock(&blk->queued_requests_lock);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,18 @@ static int block_crypto_read_func(QCryptoBlock *block,
|
||||||
BlockCrypto *crypto = bs->opaque;
|
BlockCrypto *crypto = bs->opaque;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (qemu_in_coroutine()) {
|
||||||
|
GRAPH_RDLOCK_GUARD();
|
||||||
|
|
||||||
|
ret = bdrv_co_pread(crypto->header ? crypto->header : bs->file,
|
||||||
|
offset, buflen, buf, 0);
|
||||||
|
} else {
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
|
|
||||||
ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
|
ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
|
||||||
offset, buflen, buf, 0);
|
offset, buflen, buf, 0);
|
||||||
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not read encryption header");
|
error_setg_errno(errp, -ret, "Could not read encryption header");
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -90,11 +97,18 @@ static int block_crypto_write_func(QCryptoBlock *block,
|
||||||
BlockCrypto *crypto = bs->opaque;
|
BlockCrypto *crypto = bs->opaque;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (qemu_in_coroutine()) {
|
||||||
|
GRAPH_RDLOCK_GUARD();
|
||||||
|
|
||||||
|
ret = bdrv_co_pwrite(crypto->header ? crypto->header : bs->file,
|
||||||
|
offset, buflen, buf, 0);
|
||||||
|
} else {
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
|
|
||||||
ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
|
ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
|
||||||
offset, buflen, buf, 0);
|
offset, buflen, buf, 0);
|
||||||
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not write encryption header");
|
error_setg_errno(errp, -ret, "Could not write encryption header");
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -792,7 +806,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
int64_t size;
|
int64_t size;
|
||||||
bool detached_hdr =
|
bool detached_hdr =
|
||||||
qemu_opt_get_bool(opts, "detached-header", false);
|
qemu_opt_get_bool_del(opts, "detached-header", false);
|
||||||
unsigned int cflags = 0;
|
unsigned int cflags = 0;
|
||||||
int ret;
|
int ret;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
@ -821,7 +835,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create protocol layer */
|
/* Create protocol layer */
|
||||||
ret = bdrv_co_create_file(filename, opts, errp);
|
ret = bdrv_co_create_file(filename, opts, true, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
77
block/curl.c
77
block/curl.c
|
|
@ -162,13 +162,9 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
||||||
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
||||||
void *userp, void *sp)
|
void *userp, void *sp)
|
||||||
{
|
{
|
||||||
BDRVCURLState *s;
|
BDRVCURLState *s = userp;
|
||||||
CURLState *state = NULL;
|
|
||||||
CURLSocket *socket;
|
CURLSocket *socket;
|
||||||
|
|
||||||
curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
|
|
||||||
s = state->s;
|
|
||||||
|
|
||||||
socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
|
socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
socket = g_new0(CURLSocket, 1);
|
socket = g_new0(CURLSocket, 1);
|
||||||
|
|
@ -262,8 +258,8 @@ read_end:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with s->mutex held. */
|
/* Called with s->mutex held. */
|
||||||
static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
|
static bool coroutine_fn
|
||||||
CURLAIOCB *acb)
|
curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, CURLAIOCB *acb)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
uint64_t end = start + len;
|
uint64_t end = start + len;
|
||||||
|
|
@ -311,6 +307,10 @@ static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
|
||||||
for (j=0; j<CURL_NUM_ACB; j++) {
|
for (j=0; j<CURL_NUM_ACB; j++) {
|
||||||
if (!state->acb[j]) {
|
if (!state->acb[j]) {
|
||||||
state->acb[j] = acb;
|
state->acb[j] = acb;
|
||||||
|
/* Await ongoing request */
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
qemu_mutex_lock(&s->mutex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -382,6 +382,16 @@ static void curl_multi_check_completion(BDRVCURLState *s)
|
||||||
acb->ret = error ? -EIO : 0;
|
acb->ret = error ? -EIO : 0;
|
||||||
state->acb[i] = NULL;
|
state->acb[i] = NULL;
|
||||||
qemu_mutex_unlock(&s->mutex);
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
/*
|
||||||
|
* Current AioContext is the BDS context, which may or may not
|
||||||
|
* be the request (coroutine) context.
|
||||||
|
* - If it is, the coroutine must have yielded or the FD handler
|
||||||
|
* (curl_multi_do()/curl_multi_timeout_do()) could not have
|
||||||
|
* been called and we would not be here
|
||||||
|
* - If it is not, it doesn't matter whether it has already
|
||||||
|
* yielded or not; it will be scheduled once it does yield
|
||||||
|
* So aio_co_wake() is safe to call.
|
||||||
|
*/
|
||||||
aio_co_wake(acb->co);
|
aio_co_wake(acb->co);
|
||||||
qemu_mutex_lock(&s->mutex);
|
qemu_mutex_lock(&s->mutex);
|
||||||
}
|
}
|
||||||
|
|
@ -475,11 +485,11 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
||||||
(void *)curl_read_cb) ||
|
(void *)curl_read_cb) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
|
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
|
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) ||
|
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1L) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) ||
|
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1L) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) ||
|
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1L) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
|
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) {
|
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1L)) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (s->username) {
|
if (s->username) {
|
||||||
|
|
@ -520,7 +530,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
||||||
CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
|
CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
#elif LIBCURL_VERSION_NUM >= 0x071304
|
#else
|
||||||
if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
|
if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
|
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -528,7 +538,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_VERBOSE
|
#ifdef DEBUG_VERBOSE
|
||||||
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) {
|
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1L)) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -605,6 +615,7 @@ static void curl_attach_aio_context(BlockDriverState *bs,
|
||||||
assert(!s->multi);
|
assert(!s->multi);
|
||||||
s->multi = curl_multi_init();
|
s->multi = curl_multi_init();
|
||||||
s->aio_context = new_context;
|
s->aio_context = new_context;
|
||||||
|
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
|
||||||
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
|
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
|
||||||
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
|
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
|
||||||
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
|
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
|
||||||
|
|
@ -803,7 +814,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
s->accept_range = false;
|
s->accept_range = false;
|
||||||
if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) ||
|
if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1L) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
|
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
|
curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
|
||||||
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
||||||
|
|
@ -824,22 +835,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
|
|
||||||
* know or the size is zero. From 7.19.4 CURL returns -1 if size is not
|
|
||||||
* known and zero if it is really zero-length file. */
|
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071304
|
|
||||||
if (cl < 0) {
|
if (cl < 0) {
|
||||||
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
||||||
"Server didn't report file size.");
|
"Server didn't report file size.");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
if (cl <= 0) {
|
|
||||||
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
|
||||||
"Unknown file size or zero-length file.");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s->len = cl;
|
s->len = cl;
|
||||||
|
|
||||||
|
|
@ -882,7 +882,7 @@ out_noclean:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
static void coroutine_fn curl_do_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
{
|
{
|
||||||
CURLState *state;
|
CURLState *state;
|
||||||
int running;
|
int running;
|
||||||
|
|
@ -894,10 +894,13 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
|
|
||||||
qemu_mutex_lock(&s->mutex);
|
qemu_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
// In case we have the requested data already (e.g. read-ahead),
|
/*
|
||||||
// we can just call the callback and be done.
|
* In case we have the requested data already (e.g. read-ahead),
|
||||||
|
* we can just call the callback and be done. This may have to
|
||||||
|
* await an ongoing request, in which case it itself will yield.
|
||||||
|
*/
|
||||||
if (curl_find_buf(s, start, acb->bytes, acb)) {
|
if (curl_find_buf(s, start, acb->bytes, acb)) {
|
||||||
goto out;
|
goto dont_yield;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No cache found, so let's start a new request
|
// No cache found, so let's start a new request
|
||||||
|
|
@ -912,7 +915,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
if (curl_init_state(s, state) < 0) {
|
if (curl_init_state(s, state) < 0) {
|
||||||
curl_clean_state(state);
|
curl_clean_state(state);
|
||||||
acb->ret = -EIO;
|
acb->ret = -EIO;
|
||||||
goto out;
|
goto dont_yield;
|
||||||
}
|
}
|
||||||
|
|
||||||
acb->start = 0;
|
acb->start = 0;
|
||||||
|
|
@ -927,7 +930,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
if (state->buf_len && state->orig_buf == NULL) {
|
if (state->buf_len && state->orig_buf == NULL) {
|
||||||
curl_clean_state(state);
|
curl_clean_state(state);
|
||||||
acb->ret = -ENOMEM;
|
acb->ret = -ENOMEM;
|
||||||
goto out;
|
goto dont_yield;
|
||||||
}
|
}
|
||||||
state->acb[0] = acb;
|
state->acb[0] = acb;
|
||||||
|
|
||||||
|
|
@ -939,13 +942,16 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
acb->ret = -EIO;
|
acb->ret = -EIO;
|
||||||
|
|
||||||
curl_clean_state(state);
|
curl_clean_state(state);
|
||||||
goto out;
|
goto dont_yield;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tell curl it needs to kick things off */
|
/* Tell curl it needs to kick things off */
|
||||||
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
|
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
return;
|
||||||
|
|
||||||
out:
|
dont_yield:
|
||||||
qemu_mutex_unlock(&s->mutex);
|
qemu_mutex_unlock(&s->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -961,10 +967,7 @@ static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
|
||||||
.bytes = bytes
|
.bytes = bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
curl_setup_preadv(bs, &acb);
|
curl_do_preadv(bs, &acb);
|
||||||
while (acb.ret == -EINPROGRESS) {
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
}
|
|
||||||
return acb.ret;
|
return acb.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue