Merge QEMU v7.2.4

This commit is contained in:
mborgerson 2023-07-17 04:10:07 -07:00 committed by GitHub
commit 158cc0d140
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4629 changed files with 372170 additions and 171809 deletions

View File

@ -10,9 +10,9 @@ windows_msys2_task:
memory: 8G
env:
CIRRUS_SHELL: powershell
MSYS: winsymlinks:nativestrict
MSYS: winsymlinks:native
MSYSTEM: MINGW64
MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2021-04-19/msys2-base-x86_64-20210419.sfx.exe
MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-base-x86_64-20220603.sfx.exe
MSYS2_FINGERPRINT: 0
MSYS2_PACKAGES: "
diffutils git grep make pkg-config sed
@ -32,7 +32,6 @@ windows_msys2_task:
mingw-w64-x86_64-libgcrypt
mingw-w64-x86_64-libpng
mingw-w64-x86_64-libssh
mingw-w64-x86_64-libxml2
mingw-w64-x86_64-snappy
mingw-w64-x86_64-libusb
mingw-w64-x86_64-usbredir

1
.gitattributes vendored
View File

@ -1,3 +1,4 @@
*.c.inc diff=c
*.h.inc diff=c
*.m diff=objc
*.py diff=python

View File

@ -69,7 +69,7 @@ jobs:
artifact_name: xemu-win-release
artifact_filename: xemu-win-release.zip
env:
DOCKER_IMAGE_NAME: ghcr.io/xemu-project/xemu-win64-toolchain:sha-99d540a
DOCKER_IMAGE_NAME: ghcr.io/xemu-project/xemu-win64-toolchain:sha-d0d3e7b
steps:
- name: Download source package

5
.gitignore vendored
View File

@ -1,9 +1,13 @@
/GNUmakefile
/build/
/.cache/
/.vscode/
*.pyc
.sdk
.stgit-*
.git-submodule-status
.clang-format
.gdb_history
cscope.*
tags
TAGS
@ -15,6 +19,7 @@ GTAGS
*.depend_raw
*.swp
*.patch
*.gcov
build.log
/dist

72
.gitlab-ci.d/base.yml Normal file
View File

@ -0,0 +1,72 @@
# The order of rules defined here is critically important.
# They are evaluated in order and first match wins.
#
# Thus we group them into a number of stages, ordered from
# most restrictive to least restrictive
#
.base_job_template:
rules:
#############################################################
# Stage 1: exclude scenarios where we definitely don't
# want jobs to run
#############################################################
# Cirrus jobs can't run unless the creds / target repo are set
- if: '$QEMU_JOB_CIRRUS && ($CIRRUS_GITHUB_REPO == null || $CIRRUS_API_TOKEN == null)'
when: never
# Publishing jobs should only run on the default branch in upstream
- if: '$QEMU_JOB_PUBLISH == "1" && $CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
when: never
# Non-publishing jobs should only run on staging branches in upstream
- if: '$QEMU_JOB_PUBLISH != "1" && $CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH !~ /staging/'
when: never
# Jobs only intended for forks should always be skipped on upstream
- if: '$QEMU_JOB_ONLY_FORKS == "1" && $CI_PROJECT_NAMESPACE == "qemu-project"'
when: never
# Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
when: never
# Avocado jobs don't run in forks unless $QEMU_CI_AVOCADO_TESTING is set
- if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != "qemu-project"'
when: never
#############################################################
# Stage 2: fine tune execution of jobs in specific scenarios
# where the catch all logic is inapprorpaite
#############################################################
# Optional jobs should not be run unless manually triggered
- if: '$QEMU_JOB_OPTIONAL'
when: manual
allow_failure: true
# Skipped jobs should not be run unless manually triggered
- if: '$QEMU_JOB_SKIPPED'
when: manual
allow_failure: true
# Avocado jobs can be manually start in forks if $QEMU_CI_AVOCADO_TESTING is unset
- if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != "qemu-project"'
when: manual
allow_failure: true
#############################################################
# Stage 3: catch all logic applying to any job not matching
# an earlier criteria
#############################################################
# Forks pipeline jobs don't start automatically unless
# QEMU_CI=2 is set
- if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
when: manual
# Jobs can run if any jobs they depend on were successfull
- when: on_success

View File

@ -1,4 +1,5 @@
.native_build_job_template:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
before_script:
@ -26,7 +27,8 @@
make -j"$JOBS" $MAKE_CHECK_ARGS ;
fi
.native_test_job_template:
.common_test_job_template:
extends: .base_job_template
stage: test
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
script:
@ -37,8 +39,18 @@
# Avoid recompiling by hiding ninja with NINJA=":"
- make NINJA=":" $MAKE_CHECK_ARGS
.acceptance_test_job_template:
extends: .native_test_job_template
.native_test_job_template:
extends: .common_test_job_template
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
expire_in: 7 days
paths:
- build/meson-logs/testlog.txt
reports:
junit: build/meson-logs/testlog.junit.xml
.avocado_test_job_template:
extends: .common_test_job_template
cache:
key: "${CI_JOB_NAME}-cache"
paths:
@ -67,15 +79,5 @@
after_script:
- cd build
- du -chs ${CI_PROJECT_DIR}/avocado-cache
rules:
# Only run these jobs if running on the mainstream namespace,
# or if the user set the QEMU_CI_AVOCADO_TESTING variable (either
# in its namespace setting or via git-push option, see documentation
# in /.gitlab-ci.yml of this repository).
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
when: on_success
- if: '$QEMU_CI_AVOCADO_TESTING'
when: on_success
# Otherwise, set to manual (the jobs are created but not run).
- when: manual
allow_failure: true
variables:
QEMU_JOB_AVOCADO: 1

View File

@ -24,16 +24,16 @@ check-system-alpine:
artifacts: true
variables:
IMAGE: alpine
MAKE_CHECK_ARGS: check
MAKE_CHECK_ARGS: check-unit check-qtest
acceptance-system-alpine:
extends: .acceptance_test_job_template
avocado-system-alpine:
extends: .avocado_test_job_template
needs:
- job: build-system-alpine
artifacts: true
variables:
IMAGE: alpine
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
build-system-ubuntu:
extends: .native_build_job_template
@ -41,7 +41,7 @@ build-system-ubuntu:
job: amd64-ubuntu2004-container
variables:
IMAGE: ubuntu2004
CONFIGURE_ARGS: --enable-docs --enable-fdt=system --enable-slirp=system
CONFIGURE_ARGS: --enable-docs --enable-fdt=system --enable-capstone
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
microblazeel-softmmu mips64el-softmmu
MAKE_CHECK_ARGS: check-build
@ -59,14 +59,14 @@ check-system-ubuntu:
IMAGE: ubuntu2004
MAKE_CHECK_ARGS: check
acceptance-system-ubuntu:
extends: .acceptance_test_job_template
avocado-system-ubuntu:
extends: .avocado_test_job_template
needs:
- job: build-system-ubuntu
artifacts: true
variables:
IMAGE: ubuntu2004
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
build-system-debian:
extends: .native_build_job_template
@ -74,7 +74,6 @@ build-system-debian:
job: amd64-debian-container
variables:
IMAGE: debian-amd64
CONFIGURE_ARGS: --enable-fdt=system
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
MAKE_CHECK_ARGS: check-build
@ -92,14 +91,26 @@ check-system-debian:
IMAGE: debian-amd64
MAKE_CHECK_ARGS: check
acceptance-system-debian:
extends: .acceptance_test_job_template
avocado-system-debian:
extends: .avocado_test_job_template
needs:
- job: build-system-debian
artifacts: true
variables:
IMAGE: debian-amd64
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
crash-test-debian:
extends: .native_test_job_template
needs:
- job: build-system-debian
artifacts: true
variables:
IMAGE: debian-amd64
script:
- cd build
- make NINJA=":" check-venv
- tests/venv/bin/python3 scripts/device-crash-test -q --tcg-only ./qemu-system-i386
build-system-fedora:
extends: .native_build_job_template
@ -108,7 +119,7 @@ build-system-fedora:
variables:
IMAGE: fedora
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs
--enable-fdt=system --enable-slirp=system --enable-capstone=system
--enable-fdt=system --enable-slirp --enable-capstone
TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
MAKE_CHECK_ARGS: check-build
@ -126,14 +137,27 @@ check-system-fedora:
IMAGE: fedora
MAKE_CHECK_ARGS: check
acceptance-system-fedora:
extends: .acceptance_test_job_template
avocado-system-fedora:
extends: .avocado_test_job_template
needs:
- job: build-system-fedora
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
crash-test-fedora:
extends: .native_test_job_template
needs:
- job: build-system-fedora
artifacts: true
variables:
IMAGE: fedora
script:
- cd build
- make NINJA=":" check-venv
- tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-ppc
- tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-riscv32
build-system-centos:
extends: .native_build_job_template
@ -142,7 +166,8 @@ build-system-centos:
variables:
IMAGE: centos8
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system
--enable-modules --enable-trace-backends=dtrace
--enable-modules --enable-trace-backends=dtrace --enable-docs
--enable-vfio-user-server
TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu
x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu
MAKE_CHECK_ARGS: check-build
@ -160,14 +185,14 @@ check-system-centos:
IMAGE: centos8
MAKE_CHECK_ARGS: check
acceptance-system-centos:
extends: .acceptance_test_job_template
avocado-system-centos:
extends: .avocado_test_job_template
needs:
- job: build-system-centos
artifacts: true
variables:
IMAGE: centos8
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
build-system-opensuse:
extends: .native_build_job_template
@ -192,95 +217,16 @@ check-system-opensuse:
IMAGE: opensuse-leap
MAKE_CHECK_ARGS: check
acceptance-system-opensuse:
extends: .acceptance_test_job_template
avocado-system-opensuse:
extends: .avocado_test_job_template
needs:
- job: build-system-opensuse
artifacts: true
variables:
IMAGE: opensuse-leap
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
build-disabled:
extends: .native_build_job_template
needs:
job: amd64-fedora-container
variables:
IMAGE: fedora
CONFIGURE_ARGS:
--disable-attr
--disable-auth-pam
--disable-avx2
--disable-bochs
--disable-brlapi
--disable-bzip2
--disable-cap-ng
--disable-capstone
--disable-cloop
--disable-coroutine-pool
--disable-curl
--disable-curses
--disable-dmg
--disable-docs
--disable-gcrypt
--disable-glusterfs
--disable-gnutls
--disable-gtk
--disable-guest-agent
--disable-iconv
--disable-keyring
--disable-kvm
--disable-libiscsi
--disable-libpmem
--disable-libssh
--disable-libudev
--disable-libusb
--disable-libxml2
--disable-linux-aio
--disable-live-block-migration
--disable-lzo
--disable-malloc-trim
--disable-mpath
--disable-nettle
--disable-numa
--disable-opengl
--disable-parallels
--disable-pie
--disable-qcow1
--disable-qed
--disable-qom-cast-debug
--disable-rbd
--disable-rdma
--disable-replication
--disable-sdl
--disable-seccomp
--disable-slirp
--disable-smartcard
--disable-snappy
--disable-sparse
--disable-spice
--disable-strip
--disable-tpm
--disable-usb-redir
--disable-vdi
--disable-vhost-crypto
--disable-vhost-net
--disable-vhost-scsi
--disable-vhost-kernel
--disable-vhost-user
--disable-vhost-vdpa
--disable-vhost-vsock
--disable-virglrenderer
--disable-vnc
--disable-vte
--disable-vvfat
--disable-xen
--disable-zstd
TARGETS: arm-softmmu i386-softmmu ppc64-softmmu mips64-softmmu
s390x-softmmu i386-linux-user
MAKE_CHECK_ARGS: check-qtest SPEED=slow
# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
# the configure script. The container doesn't contain Xen headers so
# Xen accelerator is not detected / selected. As result it build the
@ -305,11 +251,11 @@ build-tcg-disabled:
- cd tests/qemu-iotests/
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
170 171 183 184 192 194 208 221 222 226 227 236 253 277
170 171 183 184 192 194 208 221 226 227 236 253 277 image-fleecing
- ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122
124 132 139 142 144 145 151 152 155 157 165 194 196 200 202
208 209 216 218 222 227 234 246 247 248 250 254 255 257 258
260 261 262 263 264 270 272 273 277 279
208 209 216 218 227 234 246 247 248 250 254 255 257 258
260 261 262 263 264 270 272 273 277 279 image-fleecing
build-user:
extends: .native_build_job_template
@ -381,6 +327,7 @@ clang-user:
extends: .native_build_job_template
needs:
job: amd64-debian-user-cross-container
timeout: 70m
variables:
IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system
@ -392,12 +339,10 @@ clang-user:
# On gitlab runners, default value sometimes end up calling 2 lds concurrently and
# triggers an Out-Of-Memory error
#
# Since slirp callbacks are used in QEMU Timers, slirp needs to be compiled together
# with QEMU and linked as a static library to avoid false positives in CFI checks.
# This can be accomplished by using -enable-slirp=git, which avoids the use of
# a system-wide version of the library
# Since slirp callbacks are used in QEMU Timers, we cannot use libslirp with
# CFI builds, and thus have to disable it here.
#
# Split in three sets of build/check/acceptance to limit the execution time of each
# Split in three sets of build/check/avocado to limit the execution time of each
# job
build-cfi-aarch64:
extends: .native_build_job_template
@ -408,20 +353,18 @@ build-cfi-aarch64:
AR: llvm-ar
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
--enable-safe-stack --enable-slirp=git
--enable-safe-stack --disable-slirp
TARGETS: aarch64-softmmu
MAKE_CHECK_ARGS: check-build
timeout: 70m
# FIXME: This job is often failing, likely due to out-of-memory problems in
# the constrained containers of the shared runners. Thus this is marked as
# skipped until the situation has been solved.
QEMU_JOB_SKIPPED: 1
timeout: 90m
artifacts:
expire_in: 2 days
paths:
- build
rules:
# FIXME: This job is often failing, likely due to out-of-memory problems in
# the constrained containers of the shared runners. Thus this is marked as
# manual until the situation has been solved.
- when: manual
allow_failure: true
check-cfi-aarch64:
extends: .native_test_job_template
@ -432,14 +375,14 @@ check-cfi-aarch64:
IMAGE: fedora
MAKE_CHECK_ARGS: check
acceptance-cfi-aarch64:
extends: .acceptance_test_job_template
avocado-cfi-aarch64:
extends: .avocado_test_job_template
needs:
- job: build-cfi-aarch64
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
build-cfi-ppc64-s390x:
extends: .native_build_job_template
@ -450,20 +393,18 @@ build-cfi-ppc64-s390x:
AR: llvm-ar
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
--enable-safe-stack --enable-slirp=git
--enable-safe-stack --disable-slirp
TARGETS: ppc64-softmmu s390x-softmmu
MAKE_CHECK_ARGS: check-build
timeout: 70m
# FIXME: This job is often failing, likely due to out-of-memory problems in
# the constrained containers of the shared runners. Thus this is marked as
# skipped until the situation has been solved.
QEMU_JOB_SKIPPED: 1
timeout: 80m
artifacts:
expire_in: 2 days
paths:
- build
rules:
# FIXME: This job is often failing, likely due to out-of-memory problems in
# the constrained containers of the shared runners. Thus this is marked as
# manual until the situation has been solved.
- when: manual
allow_failure: true
check-cfi-ppc64-s390x:
extends: .native_test_job_template
@ -474,14 +415,14 @@ check-cfi-ppc64-s390x:
IMAGE: fedora
MAKE_CHECK_ARGS: check
acceptance-cfi-ppc64-s390x:
extends: .acceptance_test_job_template
avocado-cfi-ppc64-s390x:
extends: .avocado_test_job_template
needs:
- job: build-cfi-ppc64-s390x
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
build-cfi-x86_64:
extends: .native_build_job_template
@ -492,7 +433,7 @@ build-cfi-x86_64:
AR: llvm-ar
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
--enable-safe-stack --enable-slirp=git
--enable-safe-stack --disable-slirp
TARGETS: x86_64-softmmu
MAKE_CHECK_ARGS: check-build
timeout: 70m
@ -510,14 +451,14 @@ check-cfi-x86_64:
IMAGE: fedora
MAKE_CHECK_ARGS: check
acceptance-cfi-x86_64:
extends: .acceptance_test_job_template
avocado-cfi-x86_64:
extends: .avocado_test_job_template
needs:
- job: build-cfi-x86_64
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-acceptance
MAKE_CHECK_ARGS: check-avocado
tsan-build:
extends: .native_build_job_template
@ -526,37 +467,10 @@ tsan-build:
variables:
IMAGE: ubuntu2004
CONFIGURE_ARGS: --enable-tsan --cc=clang-10 --cxx=clang++-10
--enable-trace-backends=ust --enable-fdt=system --enable-slirp=system
--enable-trace-backends=ust --enable-fdt=system --disable-slirp
TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user
MAKE_CHECK_ARGS: bench V=1
# These targets are on the way out
build-deprecated:
extends: .native_build_job_template
needs:
job: amd64-debian-user-cross-container
variables:
IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools
MAKE_CHECK_ARGS: build-tcg
TARGETS: ppc64abi32-linux-user
artifacts:
expire_in: 2 days
paths:
- build
# We split the check-tcg step as test failures are expected but we still
# want to catch the build breaking.
check-deprecated:
extends: .native_test_job_template
needs:
- job: build-deprecated
artifacts: true
variables:
IMAGE: debian-all-test-cross
MAKE_CHECK_ARGS: check-tcg
allow_failure: true
# gprof/gcov are GCC features
build-gprof-gcov:
extends: .native_build_job_template
@ -580,7 +494,17 @@ check-gprof-gcov:
IMAGE: ubuntu2004
MAKE_CHECK_ARGS: check
after_script:
- ${CI_PROJECT_DIR}/scripts/ci/coverage-summary.sh
- cd build
- gcovr --xml-pretty --exclude-unreachable-branches --print-summary
-o coverage.xml --root ${CI_PROJECT_DIR} . *.p
coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
expire_in: 2 days
reports:
coverage_report:
coverage_format: cobertura
path: build/coverage.xml
build-oss-fuzz:
extends: .native_build_job_template
@ -599,8 +523,6 @@ build-oss-fuzz:
echo Testing ${fuzzer} ... ;
"${fuzzer}" -runs=1 -seed=1 || exit 1 ;
done
# Unrelated to fuzzer: run some tests with -fsanitize=address
- cd build-oss-fuzz && make check-qtest-i386 check-unit
build-tci:
extends: .native_build_job_template
@ -649,20 +571,25 @@ build-without-default-devices:
build-without-default-features:
extends: .native_build_job_template
needs:
job: amd64-debian-container
job: amd64-fedora-container
variables:
IMAGE: debian-amd64
CONFIGURE_ARGS: --without-default-features --disable-user
--target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu
MAKE_CHECK_ARGS: check-unit
IMAGE: fedora
CONFIGURE_ARGS:
--without-default-features
--disable-capstone
--disable-pie
--disable-qom-cast-debug
--disable-strip
TARGETS: avr-softmmu i386-softmmu mips64-softmmu s390x-softmmu sh4-softmmu
sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user
MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow
build-libvhost-user:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
needs:
job: amd64-fedora-container
before_script:
- dnf install -y meson ninja-build
script:
- mkdir subprojects/libvhost-user/build
- cd subprojects/libvhost-user/build
@ -675,10 +602,13 @@ build-tools-and-docs-debian:
extends: .native_build_job_template
needs:
job: amd64-debian-container
# when running on 'master' we use pre-existing container
optional: true
variables:
IMAGE: debian-amd64
MAKE_CHECK_ARGS: check-unit check-softfloat ctags TAGS cscope
MAKE_CHECK_ARGS: check-unit ctags TAGS cscope
CONFIGURE_ARGS: --disable-system --disable-user --enable-docs --enable-tools
QEMU_JOB_PUBLISH: 1
artifacts:
expire_in: 2 days
paths:
@ -698,6 +628,7 @@ build-tools-and-docs-debian:
# that users can see the results of their commits, regardless
# of what topic branch they're currently using
pages:
extends: .base_job_template
image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest
stage: test
needs:
@ -715,10 +646,5 @@ pages:
artifacts:
paths:
- public
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: on_success
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
when: never
- if: '$CI_PROJECT_NAMESPACE != "qemu-project"'
when: on_success
variables:
QEMU_JOB_PUBLISH: 1

View File

@ -11,9 +11,11 @@
# special care, because we can't just override it at the GitLab CI job
# definition level or we risk breaking it completely.
.cirrus_build_job:
extends: .base_job_template
stage: build
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
needs: []
timeout: 80m
allow_failure: true
script:
- source .gitlab-ci.d/cirrus/$NAME.vars
@ -35,12 +37,12 @@
-e "s|[@]PIP3@|$PIP3|g"
-e "s|[@]PYPI_PKGS@|$PYPI_PKGS|g"
-e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g"
-e "s|[@]TEST_TARGETSS@|$TEST_TARGETSS|g"
-e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g"
<.gitlab-ci.d/cirrus/build.yml >.gitlab-ci.d/cirrus/$NAME.yml
- cat .gitlab-ci.d/cirrus/$NAME.yml
- cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml
rules:
- if: "$CIRRUS_GITHUB_REPO && $CIRRUS_API_TOKEN"
variables:
QEMU_JOB_CIRRUS: 1
x64-freebsd-12-build:
extends: .cirrus_build_job
@ -48,14 +50,11 @@ x64-freebsd-12-build:
NAME: freebsd-12
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
CIRRUS_VM_IMAGE_SELECTOR: image_family
CIRRUS_VM_IMAGE_NAME: freebsd-12-2
CIRRUS_VM_IMAGE_NAME: freebsd-12-3
CIRRUS_VM_CPUS: 8
CIRRUS_VM_RAM: 8G
UPDATE_COMMAND: pkg update
INSTALL_COMMAND: pkg install -y
# TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed
# See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71
CONFIGURE_ARGS: --disable-gnutls
TEST_TARGETS: check
x64-freebsd-13-build:
@ -64,24 +63,61 @@ x64-freebsd-13-build:
NAME: freebsd-13
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
CIRRUS_VM_IMAGE_SELECTOR: image_family
CIRRUS_VM_IMAGE_NAME: freebsd-13-0
CIRRUS_VM_IMAGE_NAME: freebsd-13-1
CIRRUS_VM_CPUS: 8
CIRRUS_VM_RAM: 8G
UPDATE_COMMAND: pkg update
INSTALL_COMMAND: pkg install -y
TEST_TARGETS: check
x64-macos-11-base-build:
aarch64-macos-12-base-build:
extends: .cirrus_build_job
variables:
NAME: macos-11
CIRRUS_VM_INSTANCE_TYPE: osx_instance
NAME: macos-12
CIRRUS_VM_INSTANCE_TYPE: macos_instance
CIRRUS_VM_IMAGE_SELECTOR: image
CIRRUS_VM_IMAGE_NAME: big-sur-base
CIRRUS_VM_IMAGE_NAME: ghcr.io/cirruslabs/macos-monterey-base:latest
CIRRUS_VM_CPUS: 12
CIRRUS_VM_RAM: 24G
UPDATE_COMMAND: brew update
INSTALL_COMMAND: brew install
PATH_EXTRA: /usr/local/opt/ccache/libexec:/usr/local/opt/gettext/bin
PKG_CONFIG_PATH: /usr/local/opt/curl/lib/pkgconfig:/usr/local/opt/ncurses/lib/pkgconfig:/usr/local/opt/readline/lib/pkgconfig
PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin
PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
# The following jobs run VM-based tests via KVM on a Linux-based Cirrus-CI job
.cirrus_kvm_job:
extends: .base_job_template
stage: build
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
needs: []
timeout: 80m
script:
- sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g"
-e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g"
-e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g"
-e "s|[@]NAME@|$NAME|g"
-e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g"
-e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g"
<.gitlab-ci.d/cirrus/kvm-build.yml >.gitlab-ci.d/cirrus/$NAME.yml
- cat .gitlab-ci.d/cirrus/$NAME.yml
- cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml
variables:
QEMU_JOB_CIRRUS: 1
QEMU_JOB_OPTIONAL: 1
x86-netbsd:
extends: .cirrus_kvm_job
variables:
NAME: netbsd
CONFIGURE_ARGS: --target-list=x86_64-softmmu,ppc64-softmmu,aarch64-softmmu
TEST_TARGETS: check
x86-openbsd:
extends: .cirrus_kvm_job
variables:
NAME: openbsd
CONFIGURE_ARGS: --target-list=i386-softmmu,riscv64-softmmu,mips64-softmmu
TEST_TARGETS: check

View File

@ -13,6 +13,7 @@ env:
PYTHON: "@PYTHON@"
MAKE: "@MAKE@"
CONFIGURE_ARGS: "@CONFIGURE_ARGS@"
TEST_TARGETS: "@TEST_TARGETS@"
build_task:
install_script:
@ -31,5 +32,6 @@ build_task:
- $MAKE -j$(sysctl -n hw.ncpu)
- for TARGET in $TEST_TARGETS ;
do
$MAKE -j$(sysctl -n hw.ncpu) $TARGET V=1 ;
$MAKE -j$(sysctl -n hw.ncpu) $TARGET V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
done

View File

@ -2,12 +2,15 @@
#
# $ lcitool variables freebsd-12 qemu
#
# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
# https://gitlab.com/libvirt/libvirt-ci
PACKAGING_COMMAND='pkg'
CCACHE='/usr/local/bin/ccache'
CPAN_PKGS=''
CROSS_PKGS=''
MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja'
PYTHON='/usr/local/bin/python3'
PACKAGING_COMMAND='pkg'
PIP3='/usr/local/bin/pip-3.8'
PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
PYPI_PKGS=''
PYTHON='/usr/local/bin/python3'

View File

@ -2,12 +2,15 @@
#
# $ lcitool variables freebsd-13 qemu
#
# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
# https://gitlab.com/libvirt/libvirt-ci
PACKAGING_COMMAND='pkg'
CCACHE='/usr/local/bin/ccache'
CPAN_PKGS=''
CROSS_PKGS=''
MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja'
PYTHON='/usr/local/bin/python3'
PACKAGING_COMMAND='pkg'
PIP3='/usr/local/bin/pip-3.8'
PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
PYPI_PKGS=''
PYTHON='/usr/local/bin/python3'

View File

@ -0,0 +1,31 @@
container:
image: fedora:35
cpu: 4
memory: 8Gb
kvm: true
env:
CIRRUS_CLONE_DEPTH: 1
CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@"
CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@"
CI_COMMIT_SHA: "@CI_COMMIT_SHA@"
@NAME@_task:
@NAME@_vm_cache:
folder: $HOME/.cache/qemu-vm
install_script:
- dnf update -y
- dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget
clone_script:
- git clone --depth 100 "$CI_REPOSITORY_URL" .
- git fetch origin "$CI_COMMIT_REF_NAME"
- git reset --hard "$CI_COMMIT_SHA"
build_script:
- if [ -f $HOME/.cache/qemu-vm/images/@NAME@.img ]; then
make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN)
EXTRA_CONFIGURE_OPTS="@CONFIGURE_ARGS@"
BUILD_TARGET="@TEST_TARGETS@" ;
else
make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) BUILD_TARGET=help
EXTRA_CONFIGURE_OPTS="--disable-system --disable-user --disable-tools" ;
fi

View File

@ -1,15 +0,0 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool variables macos-11 qemu
#
# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
PACKAGING_COMMAND='brew'
CCACHE='/usr/local/bin/ccache'
MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja'
PYTHON='/usr/local/bin/python3'
PIP3='/usr/local/bin/pip3'
PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb libxml2 llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd'
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme virtualenv'
CPAN_PKGS='Test::Harness'

View File

@ -0,0 +1,16 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool variables macos-12 qemu
#
# https://gitlab.com/libvirt/libvirt-ci
CCACHE='/opt/homebrew/bin/ccache'
CPAN_PKGS=''
CROSS_PKGS=''
MAKE='/opt/homebrew/bin/gmake'
NINJA='/opt/homebrew/bin/ninja'
PACKAGING_COMMAND='brew'
PIP3='/opt/homebrew/bin/pip3'
PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd'
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme'
PYTHON='/opt/homebrew/bin/python3'

View File

@ -10,8 +10,3 @@ amd64-fedora-container:
extends: .container_job_template
variables:
NAME: fedora
amd64-debian10-container:
extends: .container_job_template
variables:
NAME: debian10

View File

@ -1,64 +1,49 @@
alpha-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-alpha-cross
amd64-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-amd64-cross
amd64-debian-user-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-all-test-cross
arm64-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-arm64-cross
arm64-test-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian11-container']
variables:
NAME: debian-arm64-test-cross
armel-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-armel-cross
armhf-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-armhf-cross
# We never want to build hexagon in the CI system and by default we
# always want to refer to the master registry where it lives.
hexagon-cross-container:
extends: .base_job_template
image: docker:stable
stage: containers
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
when: never
- when: always
variables:
NAME: debian-hexagon-cross
GIT_DEPTH: 1
QEMU_JOB_ONLY_FORKS: 1
services:
- docker:dind
before_script:
@ -77,92 +62,88 @@ hexagon-cross-container:
hppa-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-hppa-cross
m68k-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-m68k-cross
mips64-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-mips64-cross
mips64el-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-mips64el-cross
mips-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-mips-cross
mipsel-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-mipsel-cross
powerpc-test-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian11-container']
stage: containers
variables:
NAME: debian-powerpc-test-cross
ppc64el-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-ppc64el-cross
riscv64-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
# as we are currently based on 'sid/unstable' we may break so...
allow_failure: true
variables:
NAME: debian-riscv64-cross
# we can however build TCG tests using a non-sid base
riscv64-debian-test-cross-container:
extends: .container_job_template
stage: containers
variables:
NAME: debian-riscv64-test-cross
s390x-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-s390x-cross
sh4-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-sh4-cross
sparc64-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-sparc64-cross
tricore-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-tricore-cross

View File

@ -1,4 +1,5 @@
.container_job_template:
extends: .base_job_template
image: docker:stable
stage: containers
services:

View File

@ -7,33 +7,17 @@ amd64-alpine-container:
variables:
NAME: alpine
amd64-debian11-container:
extends: .container_job_template
variables:
NAME: debian11
amd64-debian-container:
extends: .container_job_template
stage: containers-layer2
needs: ['amd64-debian10-container']
stage: containers
variables:
NAME: debian-amd64
amd64-ubuntu1804-container:
extends: .container_job_template
variables:
NAME: ubuntu1804
amd64-ubuntu2004-container:
extends: .container_job_template
variables:
NAME: ubuntu2004
amd64-ubuntu-container:
extends: .container_job_template
variables:
NAME: ubuntu
amd64-opensuse-leap-container:
extends: .container_job_template
variables:

View File

@ -1,4 +1,5 @@
.cross_system_build_job:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
timeout: 80m
@ -14,7 +15,7 @@
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
- if grep -q "EXESUF=.exe" config-host.mak;
then make installer;
version="$(git describe --match v[0-9]*)";
version="$(git describe --match v[0-9]* 2>/dev/null || git rev-parse --short HEAD)";
mv -v qemu-setup*.exe qemu-setup-${version}.exe;
fi
@ -24,6 +25,7 @@
# KVM), and set extra options (such disabling other accelerators) via the
# $EXTRA_CONFIGURE_OPTS variable.
.cross_accel_build_job:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
timeout: 30m
@ -36,6 +38,7 @@
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
.cross_user_build_job:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
script:
@ -43,5 +46,8 @@
- cd build
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
--disable-system
--disable-system --target-list-exclude="aarch64_be-linux-user
alpha-linux-user cris-linux-user m68k-linux-user microblazeel-linux-user
nios2-linux-user or1k-linux-user ppc-linux-user sparc-linux-user
xtensa-linux-user $CROSS_SKIP_TARGETS"
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS

View File

@ -62,26 +62,14 @@ cross-i386-user:
cross-i386-tci:
extends: .cross_accel_build_job
timeout: 60m
needs:
job: i386-fedora-cross-container
variables:
IMAGE: fedora-i386-cross
ACCEL: tcg-interpreter
EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user
MAKE_CHECK_ARGS: check check-tcg
cross-mips-system:
extends: .cross_system_build_job
needs:
job: mips-debian-cross-container
variables:
IMAGE: debian-mips-cross
cross-mips-user:
extends: .cross_user_build_job
needs:
job: mips-debian-cross-container
variables:
IMAGE: debian-mips-cross
cross-mipsel-system:
extends: .cross_system_build_job
needs:
@ -124,6 +112,25 @@ cross-ppc64el-user:
variables:
IMAGE: debian-ppc64el-cross
# The riscv64 cross-builds currently use a 'sid' container to get
# compilers and libraries. Until something more stable is found we
# allow_failure so as not to block CI.
cross-riscv64-system:
extends: .cross_system_build_job
allow_failure: true
needs:
job: riscv64-debian-cross-container
variables:
IMAGE: debian-riscv64-cross
cross-riscv64-user:
extends: .cross_user_build_job
allow_failure: true
needs:
job: riscv64-debian-cross-container
variables:
IMAGE: debian-riscv64-cross
cross-s390x-system:
extends: .cross_system_build_job
needs:

View File

@ -13,226 +13,8 @@
variables:
GIT_STRATEGY: clone
# All ubuntu-18.04 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 18.04/20.04"
ubuntu-18.04-s390x-all-linux-static:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_18.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
# --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
- mkdir build
- cd build
- ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
- make --output-sync -j`nproc` check-tcg V=1
ubuntu-18.04-s390x-all:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_18.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --disable-libssh
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
ubuntu-18.04-s390x-alldbg:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_18.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-libssh
- make clean
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
ubuntu-18.04-s390x-clang:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_18.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
script:
- mkdir build
- cd build
- ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
ubuntu-18.04-s390x-tci:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_18.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --disable-libssh --enable-tcg-interpreter
- make --output-sync -j`nproc`
ubuntu-18.04-s390x-notcg:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_18.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
script:
- mkdir build
- cd build
- ../configure --disable-libssh --disable-tcg
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
# All ubuntu-20.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/qemu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 18.04/20.04"
ubuntu-20.04-aarch64-all-linux-static:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_20.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
# --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
- mkdir build
- cd build
- ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
- make --output-sync -j`nproc` check-tcg V=1
ubuntu-20.04-aarch64-all:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_20.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --disable-libssh
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
ubuntu-20.04-aarch64-alldbg:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_20.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --enable-debug --disable-libssh
- make clean
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
ubuntu-20.04-aarch64-clang:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_20.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
script:
- mkdir build
- cd build
- ../configure --disable-libssh --cc=clang-10 --cxx=clang++-10 --enable-sanitizers
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
ubuntu-20.04-aarch64-tci:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_20.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --disable-libssh --enable-tcg-interpreter
- make --output-sync -j`nproc`
ubuntu-20.04-aarch64-notcg:
allow_failure: true
needs: []
stage: build
tags:
- ubuntu_20.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
- if: "$S390X_RUNNER_AVAILABLE"
when: manual
script:
- mkdir build
- cd build
- ../configure --disable-libssh --disable-tcg
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
include:
- local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml'
- local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml'

View File

@ -0,0 +1,30 @@
centos-stream-8-x86_64:
allow_failure: true
needs: []
stage: build
tags:
- centos_stream_8
- x86_64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$CENTOS_STREAM_8_x86_64_RUNNER_AVAILABLE"
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: on_failure
expire_in: 7 days
paths:
- build/tests/results/latest/results.xml
- build/tests/results/latest/test-results
reports:
junit: build/tests/results/latest/results.xml
before_script:
- JOBS=$(expr $(nproc) + 1)
script:
- mkdir build
- cd build
- ../scripts/ci/org.centos/stream/8/x86_64/configure
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make -j"$JOBS"
- make NINJA=":" check
|| { cat meson-logs/testlog.txt; exit 1; } ;
- ../scripts/ci/org.centos/stream/8/x86_64/test-avocado

View File

@ -0,0 +1,131 @@
# All ubuntu-20.04 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 20.04/20.04"
ubuntu-20.04-s390x-all-linux-static:
needs: []
stage: build
tags:
- ubuntu_20.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
# --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
- mkdir build
- cd build
- ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
- make --output-sync -j`nproc` check-tcg V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-20.04-s390x-all:
needs: []
stage: build
tags:
- ubuntu_20.04
- s390x
timeout: 75m
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --disable-libssh
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-20.04-s390x-alldbg:
needs: []
stage: build
tags:
- ubuntu_20.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 --disable-libssh
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make clean
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-20.04-s390x-clang:
needs: []
stage: build
tags:
- ubuntu_20.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-libssh --cc=clang --cxx=clang++ --enable-sanitizers
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-20.04-s390x-tci:
needs: []
stage: build
tags:
- ubuntu_20.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-libssh --enable-tcg-interpreter
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
ubuntu-20.04-s390x-notcg:
needs: []
stage: build
tags:
- ubuntu_20.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-libssh --disable-tcg
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;

View File

@ -0,0 +1,25 @@
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/qemu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 20.04"
ubuntu-22.04-aarch32-all:
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 V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;

View File

@ -0,0 +1,130 @@
# All ubuntu-20.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/qemu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 20.04"
ubuntu-22.04-aarch64-all-linux-static:
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 --output-sync -j`nproc --ignore=40` check V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
- make --output-sync -j`nproc --ignore=40` check-tcg V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-22.04-aarch64-all:
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 V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-22.04-aarch64-alldbg:
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 V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
ubuntu-22.04-aarch64-clang:
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-10 --cxx=clang++-10 --enable-sanitizers
|| { 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 V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;
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:
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
|| { 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 V=1
|| { cat meson-logs/testlog.txt; exit 1; } ;

View File

@ -1,56 +1,85 @@
# All jobs needing docker-edk2 must use the same rules it uses.
.edk2_job_rules:
rules: # Only run this job when ...
- changes:
# this file is modified
- .gitlab-ci.d/edk2.yml
# or the Dockerfile is modified
- .gitlab-ci.d/edk2/Dockerfile
# or roms/edk2/ is modified (submodule updated)
- roms/edk2/*
when: on_success
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2'
when: on_success
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2'
when: on_success
rules:
# Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
when: never
# In forks, if QEMU_CI=1 is set, then create manual job
# if any of the files affecting the build are touched
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project"'
changes:
- .gitlab-ci.d/edk2.yml
- .gitlab-ci.d/edk2/Dockerfile
- roms/edk2/*
when: manual
# In forks, if QEMU_CI=1 is set, then create manual job
# if the branch/tag starts with 'edk2'
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_REF_NAME =~ /^edk2/'
when: manual
# In forks, if QEMU_CI=1 is set, then create manual job
# if last commit msg contains 'EDK2' (case insensitive)
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_MESSAGE =~ /edk2/i'
when: manual
# Run if any files affecting the build output are touched
- changes:
- .gitlab-ci.d/edk2.yml
- .gitlab-ci.d/edk2/Dockerfile
- roms/edk2/*
when: on_success
# Run if the branch/tag starts with 'edk2'
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/'
when: on_success
# Run if last commit msg contains 'EDK2' (case insensitive)
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i'
when: on_success
docker-edk2:
extends: .edk2_job_rules
stage: containers
image: docker:19.03.1
services:
- docker:19.03.1-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build
# We don't use TLS
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_TAG || true
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--tag $IMAGE_TAG .gitlab-ci.d/edk2
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE_TAG
extends: .edk2_job_rules
stage: containers
image: docker:19.03.1
services:
- docker:19.03.1-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build
# We don't use TLS
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_TAG || true
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--tag $IMAGE_TAG .gitlab-ci.d/edk2
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE_TAG
build-edk2:
extends: .edk2_job_rules
stage: build
needs: ['docker-edk2']
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/edk2*bz2
- pc-bios/edk2-licenses.txt
- edk2-stdout.log
- edk2-stderr.log
image: $CI_REGISTRY_IMAGE:edk2-cross-build
variables:
GIT_DEPTH: 3
script: # Clone the required submodules and build EDK2
- git submodule update --init roms/edk2
- git -C roms/edk2 submodule update --init
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
- make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2
extends: .edk2_job_rules
stage: build
needs: ['docker-edk2']
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/edk2*bz2
- pc-bios/edk2-licenses.txt
- edk2-stdout.log
- edk2-stderr.log
image: $CI_REGISTRY_IMAGE:edk2-cross-build
variables:
GIT_DEPTH: 3
script: # Clone the required submodules and build EDK2
- git submodule update --init roms/edk2
- git -C roms/edk2 submodule update --init --
ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
BaseTools/Source/C/BrotliCompress/brotli
CryptoPkg/Library/OpensslLib/openssl
MdeModulePkg/Library/BrotliCustomDecompressLib/brotli
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
- make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2

View File

@ -1,9 +1,9 @@
#
# Docker image to cross-compile EDK2 firmware binaries
#
FROM ubuntu:16.04
FROM ubuntu:18.04
MAINTAINER Philippe Mathieu-Daudé <philmd@redhat.com>
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
# Install packages required to build EDK2
RUN apt update \
@ -20,7 +20,7 @@ RUN apt update \
iasl \
make \
nasm \
python \
python3 \
uuid-dev \
&& \
\

View File

@ -1,63 +1,85 @@
# All jobs needing docker-opensbi must use the same rules it uses.
.opensbi_job_rules:
rules: # Only run this job when ...
- changes:
# this file is modified
- .gitlab-ci.d/opensbi.yml
# or the Dockerfile is modified
- .gitlab-ci.d/opensbi/Dockerfile
when: on_success
- changes: # or roms/opensbi/ is modified (submodule updated)
- roms/opensbi/*
when: on_success
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi'
when: on_success
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI'
when: on_success
rules:
# Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
when: never
# In forks, if QEMU_CI=1 is set, then create manual job
# if any files affecting the build output are touched
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project"'
changes:
- .gitlab-ci.d/opensbi.yml
- .gitlab-ci.d/opensbi/Dockerfile
- roms/opensbi/*
when: manual
# In forks, if QEMU_CI=1 is set, then create manual job
# if the branch/tag starts with 'opensbi'
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_REF_NAME =~ /^opensbi/'
when: manual
# In forks, if QEMU_CI=1 is set, then create manual job
# if the last commit msg contains 'OpenSBI' (case insensitive)
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_MESSAGE =~ /opensbi/i'
when: manual
# Run if any files affecting the build output are touched
- changes:
- .gitlab-ci.d/opensbi.yml
- .gitlab-ci.d/opensbi/Dockerfile
- roms/opensbi/*
when: on_success
# Run if the branch/tag starts with 'opensbi'
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/'
when: on_success
# Run if the last commit msg contains 'OpenSBI' (case insensitive)
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i'
when: on_success
docker-opensbi:
extends: .opensbi_job_rules
stage: containers
image: docker:19.03.1
services:
- docker:19.03.1-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build
# We don't use TLS
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_TAG || true
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--tag $IMAGE_TAG .gitlab-ci.d/opensbi
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE_TAG
extends: .opensbi_job_rules
stage: containers
image: docker:19.03.1
services:
- docker:19.03.1-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build
# We don't use TLS
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_TAG || true
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--tag $IMAGE_TAG .gitlab-ci.d/opensbi
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE_TAG
build-opensbi:
extends: .opensbi_job_rules
stage: build
needs: ['docker-opensbi']
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
- pc-bios/opensbi-riscv32-generic-fw_dynamic.elf
- pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
- pc-bios/opensbi-riscv64-generic-fw_dynamic.elf
- opensbi32-generic-stdout.log
- opensbi32-generic-stderr.log
- opensbi64-generic-stdout.log
- opensbi64-generic-stderr.log
image: $CI_REGISTRY_IMAGE:opensbi-cross-build
variables:
GIT_DEPTH: 3
script: # Clone the required submodules and build OpenSBI
- git submodule update --init roms/opensbi
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi32-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2
extends: .opensbi_job_rules
stage: build
needs: ['docker-opensbi']
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
- pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
- opensbi32-generic-stdout.log
- opensbi32-generic-stderr.log
- opensbi64-generic-stdout.log
- opensbi64-generic-stderr.log
image: $CI_REGISTRY_IMAGE:opensbi-cross-build
variables:
GIT_DEPTH: 3
script: # Clone the required submodules and build OpenSBI
- git submodule update --init roms/opensbi
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi32-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2

View File

@ -2,6 +2,7 @@
# https://gitlab.com/qemu-project/qemu/-/pipelines
include:
- local: '/.gitlab-ci.d/base.yml'
- local: '/.gitlab-ci.d/stages.yml'
- local: '/.gitlab-ci.d/edk2.yml'
- local: '/.gitlab-ci.d/opensbi.yml'
@ -11,3 +12,4 @@ include:
- local: '/.gitlab-ci.d/static_checks.yml'
- local: '/.gitlab-ci.d/custom-runners.yml'
- local: '/.gitlab-ci.d/cirrus.yml'
- local: '/.gitlab-ci.d/windows.yml'

View File

@ -3,6 +3,5 @@
# - test (for test stages, using build artefacts from a build stage)
stages:
- containers
- containers-layer2
- build
- test

View File

@ -1,32 +1,30 @@
check-patch:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/centos8:latest
needs:
job: amd64-centos8-container
image: python:3.10-alpine
needs: []
script:
- .gitlab-ci.d/check-patch.py
variables:
GIT_DEPTH: 1000
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: never
- when: on_success
allow_failure: true
QEMU_JOB_ONLY_FORKS: 1
before_script:
- apk -U add git perl
allow_failure: true
check-dco:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/centos8:latest
needs:
job: amd64-centos8-container
image: python:3.10-alpine
needs: []
script: .gitlab-ci.d/check-dco.py
variables:
GIT_DEPTH: 1000
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: never
- when: on_success
before_script:
- apk -U add git
check-python-pipenv:
extends: .base_job_template
stage: test
image: $CI_REGISTRY_IMAGE/qemu/python:latest
script:
@ -37,6 +35,7 @@ check-python-pipenv:
job: python-container
check-python-tox:
extends: .base_job_template
stage: test
image: $CI_REGISTRY_IMAGE/qemu/python:latest
script:
@ -44,6 +43,6 @@ check-python-tox:
variables:
GIT_DEPTH: 1
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
QEMU_JOB_OPTIONAL: 1
needs:
job: python-container
allow_failure: true

99
.gitlab-ci.d/windows.yml Normal file
View File

@ -0,0 +1,99 @@
.shared_msys2_builder:
extends: .base_job_template
tags:
- shared-windows
- windows
- windows-1809
cache:
key: "${CI_JOB_NAME}-cache"
paths:
- ${CI_PROJECT_DIR}/msys64/var/cache
needs: []
stage: build
timeout: 70m
before_script:
- If ( !(Test-Path -Path msys64\var\cache ) ) {
mkdir msys64\var\cache
}
- If ( !(Test-Path -Path msys64\var\cache\msys2.exe ) ) {
Invoke-WebRequest
"https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-base-x86_64-20220603.sfx.exe"
-outfile "msys64\var\cache\msys2.exe"
}
- msys64\var\cache\msys2.exe -y
- ((Get-Content -path .\msys64\etc\\post-install\\07-pacman-key.post -Raw)
-replace '--refresh-keys', '--version') |
Set-Content -Path ${CI_PROJECT_DIR}\msys64\etc\\post-install\\07-pacman-key.post
- .\msys64\usr\bin\bash -lc "sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf"
- .\msys64\usr\bin\bash -lc 'pacman --noconfirm -Syuu' # Core update
- .\msys64\usr\bin\bash -lc 'pacman --noconfirm -Syuu' # Normal update
- taskkill /F /FI "MODULES eq msys-2.0.dll"
msys2-64bit:
extends: .shared_msys2_builder
script:
- .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed
bison diffutils flex
git grep make sed
mingw-w64-x86_64-capstone
mingw-w64-x86_64-curl
mingw-w64-x86_64-cyrus-sasl
mingw-w64-x86_64-gcc
mingw-w64-x86_64-glib2
mingw-w64-x86_64-gnutls
mingw-w64-x86_64-libnfs
mingw-w64-x86_64-libpng
mingw-w64-x86_64-libssh
mingw-w64-x86_64-libtasn1
mingw-w64-x86_64-libusb
mingw-w64-x86_64-nettle
mingw-w64-x86_64-ninja
mingw-w64-x86_64-pixman
mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-python
mingw-w64-x86_64-SDL2
mingw-w64-x86_64-SDL2_image
mingw-w64-x86_64-snappy
mingw-w64-x86_64-usbredir
mingw-w64-x86_64-zstd "
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYSTEM = 'MINGW64' # Start a 64 bit Mingw environment
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- .\msys64\usr\bin\bash -lc './configure --target-list=x86_64-softmmu
--enable-capstone --without-default-devices'
- .\msys64\usr\bin\bash -lc 'make'
- .\msys64\usr\bin\bash -lc 'make check || { cat build/meson-logs/testlog.txt; exit 1; } ;'
msys2-32bit:
extends: .shared_msys2_builder
script:
- .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed
bison diffutils flex
git grep make sed
mingw-w64-i686-capstone
mingw-w64-i686-curl
mingw-w64-i686-cyrus-sasl
mingw-w64-i686-gcc
mingw-w64-i686-glib2
mingw-w64-i686-gnutls
mingw-w64-i686-gtk3
mingw-w64-i686-libgcrypt
mingw-w64-i686-libjpeg-turbo
mingw-w64-i686-libssh
mingw-w64-i686-libtasn1
mingw-w64-i686-libusb
mingw-w64-i686-lzo2
mingw-w64-i686-ninja
mingw-w64-i686-pixman
mingw-w64-i686-pkgconf
mingw-w64-i686-python
mingw-w64-i686-snappy
mingw-w64-i686-usbredir "
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYSTEM = 'MINGW32' # Start a 32-bit MinG environment
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- mkdir output
- cd output
- ..\msys64\usr\bin\bash -lc "../configure --target-list=ppc64-softmmu"
- ..\msys64\usr\bin\bash -lc 'make'
- ..\msys64\usr\bin\bash -lc 'make check || { cat meson-logs/testlog.txt; exit 1; } ;'

12
.gitmodules vendored
View File

@ -31,9 +31,6 @@
[submodule "ui/keycodemapdb"]
path = ui/keycodemapdb
url = https://gitlab.com/qemu-project/keycodemapdb.git
[submodule "capstone"]
path = capstone
url = https://gitlab.com/qemu-project/capstone.git
[submodule "roms/seabios-hppa"]
path = roms/seabios-hppa
url = https://gitlab.com/qemu-project/seabios-hppa.git
@ -49,9 +46,6 @@
[submodule "roms/edk2"]
path = roms/edk2
url = https://gitlab.com/qemu-project/edk2.git
[submodule "slirp"]
path = slirp
url = https://gitlab.com/qemu-project/libslirp.git
[submodule "roms/opensbi"]
path = roms/opensbi
url = https://gitlab.com/qemu-project/opensbi.git
@ -64,6 +58,12 @@
[submodule "roms/vbootrom"]
path = roms/vbootrom
url = https://gitlab.com/qemu-project/vbootrom.git
[submodule "tests/lcitool/libvirt-ci"]
path = tests/lcitool/libvirt-ci
url = https://gitlab.com/libvirt/libvirt-ci.git
[submodule "subprojects/libvfio-user"]
path = subprojects/libvfio-user
url = https://gitlab.com/qemu-project/libvfio-user.git
[submodule "ui/thirdparty/imgui"]
path = ui/thirdparty/imgui
url = https://github.com/ocornut/imgui.git

View File

@ -28,7 +28,11 @@ Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
# Corrupted Author fields
Aaron Larson <alarson@ddci.com> alarson@ddci.com
Andreas Färber <andreas.faerber@web.de> Andreas Färber <andreas.faerber>
Jason Wang <jasowang@redhat.com> Jason Wang <jasowang>
Marek Dolata <mkdolata@us.ibm.com> mkdolata@us.ibm.com <mkdolata@us.ibm.com>
Michael Ellerman <mpe@ellerman.id.au> michael@ozlabs.org <michael@ozlabs.org>
Nick Hudson <hnick@vmware.com> hnick@vmware.com <hnick@vmware.com>
# There is also a:
@ -50,25 +54,34 @@ Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.com>
Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com>
Frederic Konrad <konrad@adacore.com> <fred.konrad@greensocs.com>
Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.com>
Frederic Konrad <konrad.frederic@yahoo.fr> <konrad@adacore.com>
Greg Kurz <groug@kaod.org> <gkurz@linux.vnet.ibm.com>
Huacai Chen <chenhuacai@kernel.org> <chenhc@lemote.com>
Huacai Chen <chenhuacai@kernel.org> <chenhuacai@loongson.cn>
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org>
Leif Lindholm <quic_llindhol@quicinc.com> <leif.lindholm@linaro.org>
Leif Lindholm <quic_llindhol@quicinc.com> <leif@nuviainc.com>
Radoslaw Biernacki <rad@semihalf.com> <radoslaw.biernacki@linaro.org>
Paul Brook <paul@nowt.org> <paul@codesourcery.com>
Paul Burton <paulburton@kernel.org> <paul.burton@mips.com>
Paul Burton <paulburton@kernel.org> <paul.burton@imgtec.com>
Paul Burton <paulburton@kernel.org> <paul@archlinuxmips.org>
Paul Burton <paulburton@kernel.org> <pburton@wavecomp.com>
Philippe Mathieu-Daudé <philmd@linaro.org> <f4bug@amsat.org>
Philippe Mathieu-Daudé <philmd@linaro.org> <philmd@redhat.com>
Philippe Mathieu-Daudé <philmd@linaro.org> <philmd@fungible.com>
Stefan Brankovic <stefan.brankovic@syrmia.com> <stefan.brankovic@rt-rk.com.com>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
# Also list preferred name forms where people have changed their
# git author config, or had utf8/latin1 encoding issues.
Aaron Lindsay <aaron@os.amperecomputing.com>
Aaron Larson <alarson@ddci.com>
Alexey Gerasimenko <x1917x@gmail.com>
Alex Chen <alex.chen@huawei.com>
Alex Ivanov <void@aleksoft.net>
Andreas Färber <afaerber@suse.de>
Bandan Das <bsd@redhat.com>
@ -99,9 +112,11 @@ Gautham R. Shenoy <ego@in.ibm.com>
Gautham R. Shenoy <ego@linux.vnet.ibm.com>
Gonglei (Arei) <arei.gonglei@huawei.com>
Guang Wang <wang.guang55@zte.com.cn>
Haibin Zhang <haibinzhang@tencent.com>
Hailiang Zhang <zhang.zhanghailiang@huawei.com>
Hanna Reitz <hreitz@redhat.com> <mreitz@redhat.com>
Hervé Poussineau <hpoussin@reactos.org>
Hyman Huang <huangy81@chinatelecom.cn>
Jakub Jermář <jakub@jermar.eu>
Jakub Jermář <jakub.jermar@kernkonzept.com>
Jean-Christophe Dubois <jcd@tribudubois.net>
@ -135,9 +150,11 @@ Nicholas Thomas <nick@bytemark.co.uk>
Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Orit Wasserman <owasserm@redhat.com>
Paolo Bonzini <pbonzini@redhat.com>
Pan Nengyuan <pannengyuan@huawei.com>
Pavel Dovgaluk <dovgaluk@ispras.ru>
Pavel Dovgaluk <pavel.dovgaluk@gmail.com>
Pavel Dovgaluk <Pavel.Dovgaluk@ispras.ru>
Peter Chubb <peter.chubb@nicta.com.au>
Peter Crosthwaite <crosthwaite.peter@gmail.com>
Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Peter Crosthwaite <peter.crosthwaite@xilinx.com>

View File

@ -1,6 +1,3 @@
# The current Travis default is a VM based 16.04 Xenial on GCE
# Additional builds with specific requirements for a full VM need to
# be added as additional matrix: entries later on
os: linux
dist: focal
language: c
@ -190,7 +187,7 @@ jobs:
- name: "[s390x] GCC check-tcg"
arch: s390x
dist: bionic
dist: focal
addons:
apt_packages:
- libaio-dev
@ -225,7 +222,7 @@ jobs:
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
- |
if [ "$BUILD_RC" -eq 0 ] ; then
mv pc-bios/s390-ccw/*.img pc-bios/ ;
mv pc-bios/s390-ccw/*.img qemu-bundle/usr/local/share/qemu ;
${TEST_CMD} ;
else
$(exit $BUILD_RC);
@ -233,7 +230,7 @@ jobs:
- name: "[s390x] GCC (other-softmmu)"
arch: s390x
dist: bionic
dist: focal
addons:
apt_packages:
- libaio-dev
@ -263,10 +260,11 @@ jobs:
- name: "[s390x] GCC (user)"
arch: s390x
dist: bionic
dist: focal
addons:
apt_packages:
- libgcrypt20-dev
- libglib2.0-dev
- libgnutls28-dev
- ninja-build
env:
@ -274,7 +272,7 @@ jobs:
- name: "[s390x] Clang (disable-tcg)"
arch: s390x
dist: bionic
dist: focal
compiler: clang
addons:
apt_packages:
@ -305,26 +303,3 @@ jobs:
- CONFIG="--disable-containers --disable-tcg --enable-kvm
--disable-tools --host-cc=clang --cxx=clang++"
- UNRELIABLE=true
# Release builds
# The make-release script expect a QEMU version, so our tag must start with a 'v'.
# This is the case when release candidate tags are created.
- name: "Release tarball"
if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/
env:
# We want to build from the release tarball
- BUILD_DIR="release/build/dir" SRC_DIR="../../.."
- BASE_CONFIG="--prefix=$PWD/dist"
- CONFIG="--target-list=x86_64-softmmu,aarch64-softmmu,armeb-linux-user,ppc-linux-user"
- TEST_CMD="make install -j${JOBS}"
- QEMU_VERSION="${TRAVIS_TAG:1}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
script:
- make -C ${SRC_DIR} qemu-${QEMU_VERSION}.tar.bz2
- ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2
- tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION}
- mkdir -p release-build && cd release-build
- ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log meson-logs/meson-log.txt && exit 1; }
- make install
allow_failures:
- env: UNRELIABLE=true

View File

@ -22,15 +22,12 @@ config TPM
config VHOST_USER
bool
select VHOST
config VHOST_VDPA
bool
select VHOST
config VHOST_KERNEL
bool
select VHOST
config VIRTFS
bool
@ -41,3 +38,11 @@ config PVRDMA
config MULTIPROCESS_ALLOWED
bool
imply MULTIPROCESS
config FUZZ
bool
select SPARSE_MEM
config VFIO_USER_SERVER_ALLOWED
bool
imply VFIO_USER_SERVER

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,9 @@ configure: ;
ifneq ($(wildcard config-host.mak),)
include config-host.mak
include Makefile.prereqs
Makefile.prereqs: config-host.mak
git-submodule-update:
.git-submodule-status: git-submodule-update config-host.mak
Makefile: .git-submodule-status
@ -87,7 +90,7 @@ x := $(shell rm -rf meson-private meson-info meson-logs)
endif
# 1. ensure config-host.mak is up-to-date
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/QEMU_VERSION
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/QEMU_VERSION
@echo config-host.mak is out-of-date, running configure
@if test -f meson-private/coredata.dat; then \
./config.status --skip-meson; \
@ -124,6 +127,12 @@ ifneq ($(MESON),)
Makefile.mtest: build.ninja scripts/mtest2make.py
$(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@
-include Makefile.mtest
.PHONY: update-buildoptions
all update-buildoptions: $(SRC_PATH)/scripts/meson-buildoptions.sh
$(SRC_PATH)/scripts/meson-buildoptions.sh: $(SRC_PATH)/meson_options.txt
$(MESON) introspect --buildoptions $(SRC_PATH)/meson.build | $(PYTHON) \
scripts/meson-buildoptions.py > $@.tmp && mv $@.tmp $@
endif
# 4. Rules to bridge to other makefiles
@ -137,9 +146,9 @@ MAKE.q = $(findstring q,$(firstword $(filter-out --%,$(MAKEFLAGS))))
MAKE.nq = $(if $(word 2, $(MAKE.n) $(MAKE.q)),nq)
NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \
$(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \
-d keepdepfile
ninja-cmd-goals = $(or $(MAKECMDGOALS), all)
ninja-cmd-goals += $(foreach t, $(.tests), $(.test.deps.$t))
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall
# "ninja -t targets" also lists all prerequisites. If build system
@ -153,15 +162,12 @@ $(ninja-targets): run-ninja
# --output-sync line.
run-ninja: config-host.mak
ifneq ($(filter $(ninja-targets), $(ninja-cmd-goals)),)
+$(quiet-@)$(if $(MAKE.nq),@:, $(NINJA) -d keepdepfile \
$(NINJAFLAGS) $(sort $(filter $(ninja-targets), $(ninja-cmd-goals))) | cat)
+$(if $(MAKE.nq),@:,$(quiet-@)$(NINJA) $(NINJAFLAGS) \
$(sort $(filter $(ninja-targets), $(ninja-cmd-goals))) | cat)
endif
endif
# Force configure to re-run if the API symbols are updated
ifeq ($(CONFIG_PLUGIN),y)
config-host.mak: $(SRC_PATH)/plugins/qemu-plugins.symbols
.PHONY: plugins
plugins:
$(call quiet-command,\
@ -183,30 +189,27 @@ include $(SRC_PATH)/tests/Makefile.include
all: recurse-all
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
# Only keep -O and -g cflags
.PHONY: $(ROM_DIRS_RULES)
$(ROM_DIRS_RULES):
ROMS_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(ROMS)))
.PHONY: $(ROMS_RULES)
$(ROMS_RULES):
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
.PHONY: recurse-all recurse-clean
recurse-all: $(addsuffix /all, $(ROM_DIRS))
recurse-clean: $(addsuffix /clean, $(ROM_DIRS))
recurse-all: $(addsuffix /all, $(ROMS))
recurse-clean: $(addsuffix /clean, $(ROMS))
recurse-distclean: $(addsuffix /distclean, $(ROMS))
######################################################################
clean: recurse-clean
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean || :
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) clean-ctlist || :
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
find . \( -name '*.so' -o -name '*.dll' -o -name '*.[oda]' \) -type f \
find . \( -name '*.so' -o -name '*.dll' -o \
-name '*.[oda]' -o -name '*.gcno' \) -type f \
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \
-exec rm {} +
rm -f TAGS cscope.* *.pod *~ */*~
rm -f fsdev/*.pod scsi/*.pod
rm -f TAGS cscope.* *~ */*~
VERSION = $(shell cat $(SRC_PATH)/QEMU_VERSION)
@ -215,12 +218,12 @@ dist: qemu-$(VERSION).tar.bz2
qemu-%.tar.bz2:
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
distclean: clean
distclean: clean recurse-distclean
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || :
rm -f config-host.mak config-host.h* config-poison.h
rm -f tests/tcg/config-*.mak
rm -f config-all-disas.mak config.status
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f config-host.mak Makefile.prereqs qemu-bundle
rm -f tests/tcg/*/config-target.mak tests/tcg/config-host.mak
rm -f config.status
rm -f roms/seabios/config.mak
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
rm -f *-config-target.h *-config-devices.mak *-config-devices.h
rm -rf meson-private meson-logs meson-info compile_commands.json
@ -229,7 +232,8 @@ distclean: clean
rm -f linux-headers/asm
rm -Rf .sdk
find-src-path = find "$(SRC_PATH)/" -path "$(SRC_PATH)/meson" -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \)
find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \
-type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \)
.PHONY: ctags
ctags:
@ -250,7 +254,7 @@ gtags:
"GTAGS", "Remove old $@ files")
$(call quiet-command, \
(cd $(SRC_PATH) && \
$(find-src-path) | gtags -f -), \
$(find-src-path) -print | gtags -f -), \
"GTAGS", "Re-index $(SRC_PATH)")
.PHONY: TAGS
@ -280,6 +284,7 @@ cscope:
# Needed by "meson install"
export DESTDIR
include $(SRC_PATH)/tests/lcitool/Makefile.include
include $(SRC_PATH)/tests/docker/Makefile.include
include $(SRC_PATH)/tests/vm/Makefile.include
@ -309,6 +314,7 @@ endif
@echo 'Test targets:'
$(call print-help,check,Run all tests (check-help for details))
$(call print-help,bench,Run all benchmarks)
$(call print-help,lcitool-help,Help about targets for managing build environment manifests)
$(call print-help,docker-help,Help about targets running tests inside containers)
$(call print-help,vm-help,Help about targets running tests inside VM)
@echo ''

View File

@ -1 +1 @@
6.1.0
7.2.4

View File

@ -39,7 +39,7 @@ Documentation can be found hosted online at
current development version that is available at
`<https://www.qemu.org/docs/master/>`_ is generated from the ``docs/``
folder in the source tree, and is built by `Sphinx
<https://www.sphinx-doc.org/en/master/>_`.
<https://www.sphinx-doc.org/en/master/>`_.
Building
@ -59,9 +59,9 @@ of other UNIX targets. The simple steps to build QEMU are:
Additional information can also be found online via the QEMU website:
* `<https://qemu.org/Hosts/Linux>`_
* `<https://qemu.org/Hosts/Mac>`_
* `<https://qemu.org/Hosts/W32>`_
* `<https://wiki.qemu.org/Hosts/Linux>`_
* `<https://wiki.qemu.org/Hosts/Mac>`_
* `<https://wiki.qemu.org/Hosts/W32>`_
Submitting patches
@ -78,14 +78,14 @@ format-patch' and/or 'git send-email' to format & send the mail to the
qemu-devel@nongnu.org mailing list. All patches submitted must contain
a 'Signed-off-by' line from the author. Patches should follow the
guidelines set out in the `style section
<https://www.qemu.org/docs/master/devel/style.html>` of
<https://www.qemu.org/docs/master/devel/style.html>`_ of
the Developers Guide.
Additional information on submitting patches can be found online via
the QEMU website
* `<https://qemu.org/Contribute/SubmitAPatch>`_
* `<https://qemu.org/Contribute/TrivialPatches>`_
* `<https://wiki.qemu.org/Contribute/SubmitAPatch>`_
* `<https://wiki.qemu.org/Contribute/TrivialPatches>`_
The QEMU website is also maintained under source control.
@ -144,7 +144,7 @@ reported via GitLab.
For additional information on bug reporting consult:
* `<https://qemu.org/Contribute/ReportABug>`_
* `<https://wiki.qemu.org/Contribute/ReportABug>`_
ChangeLog
@ -168,4 +168,4 @@ main methods being email and IRC
Information on additional methods of contacting the community can be
found online via the QEMU website:
* `<https://qemu.org/Contribute/StartHere>`_
* `<https://wiki.qemu.org/Contribute/StartHere>`_

View File

@ -49,6 +49,14 @@ AccelClass *accel_find(const char *opt_name)
return ac;
}
/* Return the name of the current accelerator */
const char *current_accel_name(void)
{
AccelClass *ac = ACCEL_GET_CLASS(current_accel());
return ac->name;
}
static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque)
{
CPUClass *cc = CPU_CLASS(klass);
@ -121,6 +129,16 @@ bool accel_cpu_realizefn(CPUState *cpu, Error **errp)
return true;
}
int accel_supported_gdbstub_sstep_flags(void)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->gdbstub_supported_sstep_flags) {
return acc->gdbstub_supported_sstep_flags();
}
return 0;
}
static const TypeInfo accel_cpu_type = {
.name = TYPE_ACCEL_CPU,
.parent = TYPE_OBJECT,

View File

@ -66,6 +66,7 @@ void accel_init_ops_interfaces(AccelClass *ac)
{
const char *ac_name;
char *ops_name;
ObjectClass *oc;
AccelOpsClass *ops;
ac_name = object_class_get_name(OBJECT_CLASS(ac));
@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac)
ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
oc = module_object_class_by_name(ops_name);
if (!oc) {
error_report("fatal: could not load module for type '%s'", ops_name);
exit(1);
}
g_free(ops_name);
ops = ACCEL_OPS_CLASS(oc);
/*
* all accelerators need to define ops, providing at least a mandatory
* non-NULL create_vcpu_thread operation.

View File

@ -21,8 +21,6 @@
static void *dummy_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
sigset_t waitset;
int r;
rcu_register_thread();
@ -32,8 +30,13 @@ static void *dummy_cpu_thread_fn(void *arg)
cpu->can_do_io = 1;
current_cpu = cpu;
#ifndef _WIN32
sigset_t waitset;
int r;
sigemptyset(&waitset);
sigaddset(&waitset, SIG_IPI);
#endif
/* signal CPU creation */
cpu_thread_signal_created(cpu);
@ -41,6 +44,7 @@ static void *dummy_cpu_thread_fn(void *arg)
do {
qemu_mutex_unlock_iothread();
#ifndef _WIN32
do {
int sig;
r = sigwait(&waitset, &sig);
@ -49,6 +53,9 @@ static void *dummy_cpu_thread_fn(void *arg)
perror("sigwait");
exit(1);
}
#else
qemu_sem_wait(&cpu->sem);
#endif
qemu_mutex_lock_iothread();
qemu_wait_io_event(cpu);
} while (!cpu->unplug);
@ -69,4 +76,7 @@ void dummy_start_vcpu_thread(CPUState *cpu)
cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, dummy_cpu_thread_fn, cpu,
QEMU_THREAD_JOINABLE);
#ifdef _WIN32
qemu_sem_init(&cpu->sem, 0);
#endif
}

View File

@ -60,6 +60,10 @@
HVFState *hvf_state;
#ifdef __aarch64__
#define HV_VM_DEFAULT NULL
#endif
/* Memory slots */
hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
@ -116,11 +120,12 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
{
hvf_slot *mem;
MemoryRegion *area = section->mr;
bool writeable = !area->readonly && !area->rom_device;
bool writable = !area->readonly && !area->rom_device;
hv_memory_flags_t flags;
uint64_t page_size = qemu_real_host_page_size();
if (!memory_region_is_ram(area)) {
if (writeable) {
if (writable) {
return;
} else if (!memory_region_is_romd(area)) {
/*
@ -131,6 +136,12 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
}
}
if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
!QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
/* Not page aligned, so we can not map as RAM */
add = false;
}
mem = hvf_find_overlap_slot(
section->offset_within_address_space,
int128_get64(section->size));
@ -239,12 +250,12 @@ static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
if (on) {
slot->flags |= HVF_SLOT_LOG;
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
HV_MEMORY_READ);
HV_MEMORY_READ | HV_MEMORY_EXEC);
/* stop tracking region*/
} else {
slot->flags &= ~HVF_SLOT_LOG;
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
HV_MEMORY_READ | HV_MEMORY_WRITE);
HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
}
}
@ -291,6 +302,7 @@ static void hvf_region_del(MemoryListener *listener,
}
static MemoryListener hvf_memory_listener = {
.name = "hvf",
.priority = 10,
.region_add = hvf_region_add,
.region_del = hvf_region_del,
@ -316,7 +328,7 @@ static int hvf_accel_init(MachineState *ms)
s = g_new0(HVFState, 1);
s->num_slots = 32;
s->num_slots = ARRAY_SIZE(s->slots);
for (x = 0; x < s->num_slots; ++x) {
s->slots[x].size = 0;
s->slots[x].slot_id = x;
@ -324,7 +336,8 @@ static int hvf_accel_init(MachineState *ms)
hvf_state = s;
memory_listener_register(&hvf_memory_listener, &address_space_memory);
return 0;
return hvf_arch_init();
}
static void hvf_accel_class_init(ObjectClass *oc, void *data)
@ -365,17 +378,20 @@ static int hvf_init_vcpu(CPUState *cpu)
cpu->hvf = g_malloc0(sizeof(*cpu->hvf));
/* init cpu signals */
sigset_t set;
struct sigaction sigact;
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = dummy_signal;
sigaction(SIG_IPI, &sigact, NULL);
pthread_sigmask(SIG_BLOCK, NULL, &set);
sigdelset(&set, SIG_IPI);
pthread_sigmask(SIG_BLOCK, NULL, &cpu->hvf->unblock_ipi_mask);
sigdelset(&cpu->hvf->unblock_ipi_mask, SIG_IPI);
#ifdef __aarch64__
r = hv_vcpu_create(&cpu->hvf->fd, (hv_vcpu_exit_t **)&cpu->hvf->exit, NULL);
#else
r = hv_vcpu_create((hv_vcpuid_t *)&cpu->hvf->fd, HV_VCPU_DEFAULT);
#endif
cpu->vcpu_dirty = 1;
assert_hvf_ok(r);
@ -451,6 +467,7 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = hvf_start_vcpu_thread;
ops->kick_vcpu_thread = hvf_kick_vcpu_thread;
ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset;
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;

View File

@ -9,7 +9,6 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "sysemu/hvf.h"
#include "sysemu/hvf_int.h"

View File

@ -16,12 +16,14 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "sysemu/kvm.h"
#include "sysemu/kvm_int.h"
#include "sysemu/runstate.h"
#include "sysemu/cpus.h"
#include "qemu/guest-random.h"
#include "qapi/error.h"
#include <linux/kvm.h>
#include "kvm-cpus.h"
static void *kvm_vcpu_thread_fn(void *arg)
@ -74,15 +76,34 @@ static void kvm_start_vcpu_thread(CPUState *cpu)
cpu, QEMU_THREAD_JOINABLE);
}
static bool kvm_vcpu_thread_is_idle(CPUState *cpu)
{
return !kvm_halt_in_kernel();
}
static bool kvm_cpus_are_resettable(void)
{
return !kvm_enabled() || kvm_cpu_check_are_resettable();
}
static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = kvm_start_vcpu_thread;
ops->cpu_thread_is_idle = kvm_vcpu_thread_is_idle;
ops->cpus_are_resettable = kvm_cpus_are_resettable;
ops->synchronize_post_reset = kvm_cpu_synchronize_post_reset;
ops->synchronize_post_init = kvm_cpu_synchronize_post_init;
ops->synchronize_state = kvm_cpu_synchronize_state;
ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm;
#ifdef KVM_CAP_SET_GUEST_DEBUG
ops->supports_guest_debug = kvm_supports_guest_debug;
ops->insert_breakpoint = kvm_insert_breakpoint;
ops->remove_breakpoint = kvm_remove_breakpoint;
ops->remove_all_breakpoints = kvm_remove_all_breakpoints;
#endif
}
static const TypeInfo kvm_accel_ops_type = {

File diff suppressed because it is too large Load Diff

View File

@ -18,5 +18,9 @@ void kvm_destroy_vcpu(CPUState *cpu);
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
void kvm_cpu_synchronize_post_init(CPUState *cpu);
void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu);
bool kvm_supports_guest_debug(void);
int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len);
int kvm_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len);
void kvm_remove_all_breakpoints(CPUState *cpu);
#endif /* KVM_CPUS_H */

View File

@ -3,6 +3,5 @@ kvm_ss.add(files(
'kvm-all.c',
'kvm-accel-ops.c',
))
kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c'))
specific_ss.add_all(when: 'CONFIG_KVM', if_true: kvm_ss)

View File

@ -2,17 +2,19 @@ specific_ss.add(files('accel-common.c'))
softmmu_ss.add(files('accel-softmmu.c'))
user_ss.add(files('accel-user.c'))
subdir('hvf')
subdir('qtest')
subdir('kvm')
subdir('tcg')
subdir('xen')
subdir('stubs')
if have_system
subdir('hvf')
subdir('qtest')
subdir('kvm')
subdir('xen')
subdir('stubs')
endif
dummy_ss = ss.source_set()
dummy_ss.add(files(
'dummy-cpus.c',
))
specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: dummy_ss)
specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: dummy_ss)
specific_ss.add_all(when: ['CONFIG_XEN'], if_true: dummy_ss)

View File

@ -1,2 +1 @@
qtest_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'],
if_true: files('qtest.c'))
qtest_module_ss.add(when: ['CONFIG_SOFTMMU'], if_true: files('qtest.c'))

View File

@ -20,7 +20,6 @@
#include "qemu/accel.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "qemu/guest-random.h"
#include "qemu/main-loop.h"
#include "hw/core/cpu.h"

View File

@ -16,6 +16,8 @@
#include "qemu/osdep.h"
#include "sysemu/hax.h"
bool hax_allowed;
int hax_sync_vcpus(void)
{
return 0;

View File

@ -12,10 +12,7 @@
#include "qemu/osdep.h"
#include "sysemu/kvm.h"
#ifndef CONFIG_USER_ONLY
#include "hw/pci/msi.h"
#endif
KVMState *kvm_state;
bool kvm_kernel_irqchip;
@ -49,27 +46,6 @@ int kvm_has_many_ioeventfds(void)
return 0;
}
int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
{
return -ENOSYS;
}
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type)
{
return -EINVAL;
}
int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type)
{
return -EINVAL;
}
void kvm_remove_all_breakpoints(CPUState *cpu)
{
}
int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
{
return 1;
@ -80,8 +56,7 @@ int kvm_on_sigbus(int code, void *addr)
return 1;
}
#ifndef CONFIG_USER_ONLY
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
{
return -ENOSYS;
}
@ -147,4 +122,13 @@ bool kvm_arm_supports_user_irq(void)
{
return false;
}
#endif
bool kvm_dirty_ring_enabled(void)
{
return false;
}
uint32_t kvm_dirty_ring_size(void)
{
return 0;
}

View File

@ -1,4 +1,7 @@
specific_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c'))
specific_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
specific_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
specific_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
sysemu_stubs_ss = ss.source_set()
sysemu_stubs_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c'))
sysemu_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
sysemu_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
sysemu_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: sysemu_stubs_ss)

View File

@ -21,6 +21,17 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{
}
void tcg_flush_jmp_cache(CPUState *cpu)
{
}
int probe_access_flags(CPUArchState *env, target_ulong addr,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr)
{
g_assert_not_reached();
}
void *probe_access(CPUArchState *env, target_ulong addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
@ -28,12 +39,12 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
g_assert_not_reached();
}
void QEMU_NORETURN cpu_loop_exit(CPUState *cpu)
G_NORETURN void cpu_loop_exit(CPUState *cpu)
{
g_assert_not_reached();
}
void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
{
g_assert_not_reached();
}

View File

@ -13,56 +13,23 @@
* See the COPYING file in the top-level directory.
*/
static uint16_t atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi)
{
CPUState *cpu = env_cpu(env);
uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false);
trace_guest_mem_before_exec(cpu, addr, info);
trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST);
return info;
}
static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr,
uint16_t info)
MemOpIdx oi)
{
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info | TRACE_MEM_ST);
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_RW);
}
#if HAVE_ATOMIC128
static uint16_t atomic_trace_ld_pre(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi)
{
uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false);
trace_guest_mem_before_exec(env_cpu(env), addr, info);
return info;
}
static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr,
uint16_t info)
MemOpIdx oi)
{
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
}
static uint16_t atomic_trace_st_pre(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi)
{
uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), true);
trace_guest_mem_before_exec(env_cpu(env), addr, info);
return info;
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R);
}
static void atomic_trace_st_post(CPUArchState *env, target_ulong addr,
uint16_t info)
MemOpIdx oi)
{
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W);
}
#endif

View File

@ -19,7 +19,6 @@
*/
#include "qemu/plugin.h"
#include "trace/mem.h"
#if DATA_SIZE == 16
# define SUFFIX o
@ -64,7 +63,7 @@
the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1
# define END
#elif defined(HOST_WORDS_BIGENDIAN)
#elif HOST_BIG_ENDIAN
# define END _be
#else
# define END _le
@ -72,12 +71,11 @@
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE cmpv, ABI_TYPE newv,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_READ | PAGE_WRITE, retaddr);
DATA_TYPE ret;
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, cmpv, newv);
@ -85,64 +83,60 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv);
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
atomic_trace_rmw_post(env, addr, oi);
return ret;
}
#if DATA_SIZE >= 16
#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_READ, retaddr);
DATA_TYPE val;
uint16_t info = atomic_trace_ld_pre(env, addr, oi);
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
atomic_trace_ld_post(env, addr, oi);
return val;
}
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_WRITE, retaddr);
uint16_t info = atomic_trace_st_pre(env, addr, oi);
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
atomic_trace_st_post(env, addr, oi);
}
#endif
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_READ | PAGE_WRITE, retaddr);
DATA_TYPE ret;
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
ret = qatomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
atomic_trace_rmw_post(env, addr, oi);
return ret;
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
{ \
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
PAGE_READ | PAGE_WRITE, retaddr); \
DATA_TYPE ret; \
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
ret = qatomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
atomic_trace_rmw_post(env, addr, oi); \
return ret; \
}
@ -167,12 +161,11 @@ GEN_ATOMIC_HELPER(xor_fetch)
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval, TCGMemOpIdx oi, uintptr_t retaddr) \
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
{ \
XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
PAGE_READ | PAGE_WRITE, retaddr); \
XDATA_TYPE cmp, old, new, val = xval; \
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
smp_mb(); \
cmp = qatomic_read__nocheck(haddr); \
do { \
@ -180,7 +173,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
cmp = qatomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
atomic_trace_rmw_post(env, addr, oi); \
return RET; \
}
@ -203,7 +196,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
/* Define reverse-host-endian atomic operations. Note that END is used
within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN
#if HOST_BIG_ENDIAN
# define END _le
#else
# define END _be
@ -211,12 +204,11 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE cmpv, ABI_TYPE newv,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_READ | PAGE_WRITE, retaddr);
DATA_TYPE ret;
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
@ -224,65 +216,61 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
atomic_trace_rmw_post(env, addr, oi);
return BSWAP(ret);
}
#if DATA_SIZE >= 16
#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_READ, retaddr);
DATA_TYPE val;
uint16_t info = atomic_trace_ld_pre(env, addr, oi);
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
atomic_trace_ld_post(env, addr, oi);
return BSWAP(val);
}
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_WRITE, retaddr);
uint16_t info = atomic_trace_st_pre(env, addr, oi);
val = BSWAP(val);
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
atomic_trace_st_post(env, addr, oi);
}
#endif
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
PAGE_READ | PAGE_WRITE, retaddr);
ABI_TYPE ret;
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
atomic_trace_rmw_post(env, addr, oi);
return BSWAP(ret);
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
{ \
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
PAGE_READ | PAGE_WRITE, retaddr); \
DATA_TYPE ret; \
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
ret = qatomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
atomic_trace_rmw_post(env, addr, oi); \
return BSWAP(ret); \
}
@ -304,12 +292,11 @@ GEN_ATOMIC_HELPER(xor_fetch)
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval, TCGMemOpIdx oi, uintptr_t retaddr) \
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
{ \
XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
PAGE_READ | PAGE_WRITE, retaddr); \
XDATA_TYPE ldo, ldn, old, new, val = xval; \
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
smp_mb(); \
ldn = qatomic_read__nocheck(haddr); \
do { \
@ -317,7 +304,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
atomic_trace_rmw_post(env, addr, oi); \
return RET; \
}

View File

@ -71,7 +71,7 @@ void cpu_loop_exit(CPUState *cpu)
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
{
if (pc) {
cpu_restore_state(cpu, pc, true);
cpu_restore_state(cpu, pc);
}
cpu_loop_exit(cpu);
}

View File

@ -18,8 +18,10 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/qemu-print.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-machine.h"
#include "qapi/type-helpers.h"
#include "hw/core/tcg-cpu-ops.h"
#include "trace.h"
#include "disas/disas.h"
@ -38,7 +40,9 @@
#include "exec/cpu-all.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/replay.h"
#include "sysemu/tcg.h"
#include "exec/helper-proto.h"
#include "tb-jmp-cache.h"
#include "tb-hash.h"
#include "tb-context.h"
#include "internal.h"
@ -167,22 +171,123 @@ uint32_t curr_cflags(CPUState *cpu)
return cflags;
}
struct tb_desc {
target_ulong pc;
target_ulong cs_base;
CPUArchState *env;
tb_page_addr_t page_addr0;
uint32_t flags;
uint32_t cflags;
uint32_t trace_vcpu_dstate;
};
static bool tb_lookup_cmp(const void *p, const void *d)
{
const TranslationBlock *tb = p;
const struct tb_desc *desc = d;
if ((TARGET_TB_PCREL || tb_pc(tb) == desc->pc) &&
tb_page_addr0(tb) == desc->page_addr0 &&
tb->cs_base == desc->cs_base &&
tb->flags == desc->flags &&
tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
(tb_cflags(tb) & ~CF_INVALID) == desc->cflags) {
/* check next page if needed */
tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb);
if (tb_phys_page1 == -1) {
return true;
} else {
tb_page_addr_t phys_page1;
target_ulong virt_page1;
/*
* We know that the first page matched, and an otherwise valid TB
* encountered an incomplete instruction at the end of that page,
* therefore we know that generating a new TB from the current PC
* must also require reading from the next page -- even if the
* second pages do not match, and therefore the resulting insn
* is different for the new TB. Therefore any exception raised
* here by the faulting lookup is not premature.
*/
virt_page1 = TARGET_PAGE_ALIGN(desc->pc);
phys_page1 = get_page_addr_code(desc->env, virt_page1);
if (tb_phys_page1 == phys_page1) {
return true;
}
}
}
return false;
}
static TranslationBlock *
tb_htable_lookup_common(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
uint32_t cflags, const struct qht *ht,
qht_lookup_func_t func)
{
tb_page_addr_t phys_pc;
struct tb_desc desc;
uint32_t h;
desc.env = cpu->env_ptr;
desc.cs_base = cs_base;
desc.flags = flags;
desc.cflags = cflags;
desc.trace_vcpu_dstate = *cpu->trace_dstate;
desc.pc = pc;
phys_pc = get_page_addr_code(desc.env, pc);
if (phys_pc == -1) {
return NULL;
}
desc.page_addr0 = phys_pc;
h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : pc),
flags, cflags, *cpu->trace_dstate);
return qht_lookup_custom(ht, &desc, h, func);
}
TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
uint32_t cflags)
{
return tb_htable_lookup_common(cpu, pc, cs_base, flags, cflags,
&tb_ctx.htable, tb_lookup_cmp);
}
static bool inv_tb_lookup_cmp(const void *p, const void *d)
{
const TranslationBlock *tb = p;
const struct tb_desc *desc = d;
return tb_lookup_cmp(p, d) &&
tb->ihash == tb_code_hash_func(desc->env, desc->pc, tb->size);
}
TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
uint32_t cflags)
{
return tb_htable_lookup_common(cpu, pc, cs_base, flags, cflags,
&tb_ctx.inv_htable, inv_tb_lookup_cmp);
}
/* Might cause an exception, so have a longjmp destination ready */
static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base,
uint32_t flags, uint32_t cflags)
{
TranslationBlock *tb;
CPUJumpCache *jc;
uint32_t hash;
/* we should never be trying to look up an INVALID tb */
tcg_debug_assert(!(cflags & CF_INVALID));
hash = tb_jmp_cache_hash_func(pc);
tb = qatomic_rcu_read(&cpu->tb_jmp_cache[hash]);
jc = cpu->tb_jmp_cache;
tb = tb_jmp_cache_get_tb(jc, hash);
if (likely(tb &&
tb->pc == pc &&
tb_jmp_cache_get_pc(jc, hash, tb) == pc &&
tb->cs_base == cs_base &&
tb->flags == flags &&
tb->trace_vcpu_dstate == *cpu->trace_dstate &&
@ -193,16 +298,14 @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
if (tb == NULL) {
return NULL;
}
qatomic_set(&cpu->tb_jmp_cache[hash], tb);
tb_jmp_cache_set(jc, hash, tb, pc);
return tb;
}
static inline void log_cpu_exec(target_ulong pc, CPUState *cpu,
const TranslationBlock *tb)
static void log_cpu_exec(target_ulong pc, CPUState *cpu,
const TranslationBlock *tb)
{
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC))
&& qemu_log_in_addr_range(pc)) {
if (qemu_log_in_addr_range(pc)) {
qemu_log_mask(CPU_LOG_EXEC,
"Trace %d: %p [" TARGET_FMT_lx
"/" TARGET_FMT_lx "/%08x/%08x] %s\n",
@ -211,32 +314,30 @@ static inline void log_cpu_exec(target_ulong pc, CPUState *cpu,
#if defined(DEBUG_DISAS)
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
FILE *logfile = qemu_log_lock();
int flags = 0;
FILE *logfile = qemu_log_trylock();
if (logfile) {
int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
}
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
}
#if defined(TARGET_I386)
flags |= CPU_DUMP_CCOP;
flags |= CPU_DUMP_CCOP;
#endif
log_cpu_state(cpu, flags);
qemu_log_unlock(logfile);
cpu_dump_state(cpu, logfile, flags);
qemu_log_unlock(logfile);
}
}
#endif /* DEBUG_DISAS */
}
}
static bool check_for_breakpoints(CPUState *cpu, target_ulong pc,
uint32_t *cflags)
static bool check_for_breakpoints_slow(CPUState *cpu, target_ulong pc,
uint32_t *cflags)
{
CPUBreakpoint *bp;
bool match_page = false;
if (likely(QTAILQ_EMPTY(&cpu->breakpoints))) {
return false;
}
/*
* Singlestep overrides breakpoints.
* This requirement is visible in the record-replay tests, where
@ -297,6 +398,13 @@ static bool check_for_breakpoints(CPUState *cpu, target_ulong pc,
return false;
}
static inline bool check_for_breakpoints(CPUState *cpu, target_ulong pc,
uint32_t *cflags)
{
return unlikely(!QTAILQ_EMPTY(&cpu->breakpoints)) &&
check_for_breakpoints_slow(cpu, pc, cflags);
}
/**
* helper_lookup_tb_ptr: quick check for next tb
* @env: current cpu state
@ -324,7 +432,9 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
return tcg_code_gen_epilogue;
}
log_cpu_exec(pc, cpu, tb);
if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) {
log_cpu_exec(pc, cpu, tb);
}
return tb->tc.ptr;
}
@ -347,7 +457,9 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
TranslationBlock *last_tb;
const void *tb_ptr = itb->tc.ptr;
log_cpu_exec(itb->pc, cpu, itb);
if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) {
log_cpu_exec(log_pc(cpu, itb), cpu, itb);
}
qemu_thread_jit_execute();
ret = tcg_qemu_tb_exec(env, tb_ptr);
@ -371,18 +483,34 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
* of the start of the TB.
*/
CPUClass *cc = CPU_GET_CLASS(cpu);
qemu_log_mask_and_addr(CPU_LOG_EXEC, last_tb->pc,
"Stopped execution of TB chain before %p ["
TARGET_FMT_lx "] %s\n",
last_tb->tc.ptr, last_tb->pc,
lookup_symbol(last_tb->pc));
if (cc->tcg_ops->synchronize_from_tb) {
cc->tcg_ops->synchronize_from_tb(cpu, last_tb);
} else {
assert(!TARGET_TB_PCREL);
assert(cc->set_pc);
cc->set_pc(cpu, last_tb->pc);
cc->set_pc(cpu, tb_pc(last_tb));
}
if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
target_ulong pc = log_pc(cpu, last_tb);
if (qemu_log_in_addr_range(pc)) {
qemu_log("Stopped execution of TB chain before %p ["
TARGET_FMT_lx "] %s\n",
last_tb->tc.ptr, pc, lookup_symbol(pc));
}
}
}
/*
* If gdb single-step, and we haven't raised another exception,
* raise a debug exception. Single-step with another exception
* is handled in cpu_handle_exception.
*/
if (unlikely(cpu->singlestep_enabled) && cpu->exception_index == -1) {
cpu->exception_index = EXCP_DEBUG;
cpu_loop_exit(cpu);
}
return last_tb;
}
@ -407,7 +535,7 @@ static void cpu_exec_exit(CPUState *cpu)
void cpu_exec_step_atomic(CPUState *cpu)
{
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
CPUArchState *env = cpu->env_ptr;
TranslationBlock *tb;
target_ulong cs_base, pc;
uint32_t flags, cflags;
@ -446,12 +574,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
cpu_tb_exec(cpu, tb, &tb_exit);
cpu_exec_exit(cpu);
} else {
/*
* The mmap_lock is dropped by tb_gen_code if it runs out of
* memory.
*/
#ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock());
clear_helper_retaddr();
if (have_mmap_lock()) {
mmap_unlock();
}
#endif
if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread();
@ -460,7 +587,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
qemu_plugin_disable_mem_helpers(cpu);
}
/*
* As we start the exclusive region before codegen we must still
* be in the region if we longjump out of either the codegen or
@ -471,93 +597,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
end_exclusive();
}
struct tb_desc {
target_ulong pc;
target_ulong cs_base;
CPUArchState *env;
tb_page_addr_t phys_page1;
uint32_t flags;
uint32_t cflags;
uint32_t trace_vcpu_dstate;
};
static bool tb_lookup_cmp(const void *p, const void *d)
{
const TranslationBlock *tb = p;
const struct tb_desc *desc = d;
if (tb->pc == desc->pc &&
tb->page_addr[0] == desc->phys_page1 &&
tb->cs_base == desc->cs_base &&
tb->flags == desc->flags &&
tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
(tb_cflags(tb) & ~CF_INVALID) == desc->cflags) {
/* check next page if needed */
if (tb->page_addr[1] == -1) {
return true;
} else {
tb_page_addr_t phys_page2;
target_ulong virt_page2;
virt_page2 = (desc->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
phys_page2 = get_page_addr_code(desc->env, virt_page2);
if (tb->page_addr[1] == phys_page2) {
return true;
}
}
}
return false;
}
static bool inv_tb_lookup_cmp(const void *p, const void *d)
{
const TranslationBlock *tb = p;
const struct tb_desc *desc = d;
return tb_lookup_cmp(p, d) &&
tb->ihash == tb_code_hash_func(desc->env, tb->pc, tb->size);
}
static TranslationBlock *
tb_htable_lookup_common(CPUState *cpu, target_ulong pc, target_ulong cs_base,
uint32_t flags, uint32_t cflags, const struct qht *ht,
qht_lookup_func_t func)
{
tb_page_addr_t phys_pc;
struct tb_desc desc;
uint32_t h;
desc.env = (CPUArchState *)cpu->env_ptr;
desc.cs_base = cs_base;
desc.flags = flags;
desc.cflags = cflags;
desc.trace_vcpu_dstate = *cpu->trace_dstate;
desc.pc = pc;
phys_pc = get_page_addr_code(desc.env, pc);
if (phys_pc == -1) {
return NULL;
}
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
h = tb_hash_func(phys_pc, pc, flags, cflags, *cpu->trace_dstate);
return qht_lookup_custom(ht, &desc, h, func);
}
TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
uint32_t cflags)
{
return tb_htable_lookup_common(cpu, pc, cs_base, flags, cflags,
&tb_ctx.htable, tb_lookup_cmp);
}
TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
uint32_t cflags)
{
return tb_htable_lookup_common(cpu, pc, cs_base, flags, cflags,
&tb_ctx.inv_htable, inv_tb_lookup_cmp);
}
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
{
if (TCG_TARGET_HAS_direct_jump) {
@ -600,11 +639,8 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
qemu_spin_unlock(&tb_next->jmp_lock);
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
"Linking TBs %p [" TARGET_FMT_lx
"] index %d -> %p [" TARGET_FMT_lx "]\n",
tb->tc.ptr, tb->pc, n,
tb_next->tc.ptr, tb_next->pc);
qemu_log_mask(CPU_LOG_EXEC, "Linking TBs %p index %d -> %p\n",
tb->tc.ptr, n, tb_next->tc.ptr);
return;
out_unlock_next:
@ -614,8 +650,9 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
static inline bool cpu_handle_halt(CPUState *cpu)
{
#ifndef CONFIG_USER_ONLY
if (cpu->halted) {
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
#if defined(TARGET_I386)
if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
X86CPU *x86_cpu = X86_CPU(cpu);
qemu_mutex_lock_iothread();
@ -623,13 +660,14 @@ static inline bool cpu_handle_halt(CPUState *cpu)
cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
qemu_mutex_unlock_iothread();
}
#endif
#endif /* TARGET_I386 */
if (!cpu_has_work(cpu)) {
return true;
}
cpu->halted = 0;
}
#endif /* !CONFIG_USER_ONLY */
return false;
}
@ -657,7 +695,8 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
if (replay_has_exception()
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) {
/* Execute just one insn to trigger exception pending in the log */
cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT) | 1;
cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT)
| CF_NOIRQ | 1;
}
#endif
return false;
@ -677,8 +716,8 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
loop */
#if defined(TARGET_I386)
CPUClass *cc = CPU_GET_CLASS(cpu);
cc->tcg_ops->do_interrupt(cpu);
#endif
cc->tcg_ops->fake_user_interrupt(cpu);
#endif /* TARGET_I386 */
*ret = cpu->exception_index;
cpu->exception_index = -1;
return true;
@ -711,6 +750,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
return false;
}
#ifndef CONFIG_USER_ONLY
/*
* CPU_INTERRUPT_POLL is a virtual event which gets converted into a
* "real" interrupt event later. It does not need to be recorded for
@ -724,11 +764,19 @@ static inline bool need_replay_interrupt(int interrupt_request)
return true;
#endif
}
#endif /* !CONFIG_USER_ONLY */
static inline bool cpu_handle_interrupt(CPUState *cpu,
TranslationBlock **last_tb)
{
CPUClass *cc = CPU_GET_CLASS(cpu);
/*
* If we have requested custom cflags with CF_NOIRQ we should
* skip checking here. Any pending interrupts will get picked up
* by the next TB we execute under normal cflags.
*/
if (cpu->cflags_next_tb != -1 && cpu->cflags_next_tb & CF_NOIRQ) {
return false;
}
/* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request.
@ -751,6 +799,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
qemu_mutex_unlock_iothread();
return true;
}
#if !defined(CONFIG_USER_ONLY)
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
/* Do nothing */
} else if (interrupt_request & CPU_INTERRUPT_HALT) {
@ -779,12 +828,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
qemu_mutex_unlock_iothread();
return true;
}
#endif
#endif /* !TARGET_I386 */
/* The target hook has 3 exit conditions:
False when the interrupt isn't processed,
True when it is, and we should restart on a new TB,
and via longjmp via cpu_loop_exit. */
else {
CPUClass *cc = CPU_GET_CLASS(cpu);
if (cc->tcg_ops->cpu_exec_interrupt &&
cc->tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) {
if (need_replay_interrupt(interrupt_request)) {
@ -795,14 +846,19 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
* raised when single-stepping so that GDB doesn't miss the
* next instruction.
*/
cpu->exception_index =
(cpu->singlestep_enabled ? EXCP_DEBUG : -1);
if (unlikely(cpu->singlestep_enabled)) {
cpu->exception_index = EXCP_DEBUG;
qemu_mutex_unlock_iothread();
return true;
}
cpu->exception_index = -1;
*last_tb = NULL;
}
/* The target hook may have updated the 'cpu->interrupt_request';
* reload the 'interrupt_request' value */
interrupt_request = cpu->interrupt_request;
}
#endif /* !CONFIG_USER_ONLY */
if (interrupt_request & CPU_INTERRUPT_EXITTB) {
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
/* ensure that no TB jump will be modified as
@ -830,11 +886,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
}
static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
target_ulong pc,
TranslationBlock **last_tb, int *tb_exit)
{
int32_t insns_left;
trace_exec_tb(tb, tb->pc);
trace_exec_tb(tb, pc);
tb = cpu_tb_exec(cpu, tb, tb_exit);
if (*tb_exit != TB_EXIT_REQUESTED) {
*last_tb = tb;
@ -925,7 +982,10 @@ int cpu_exec(CPUState *cpu)
#endif
#ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock());
clear_helper_retaddr();
if (have_mmap_lock()) {
mmap_unlock();
}
#endif
if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread();
@ -967,6 +1027,8 @@ int cpu_exec(CPUState *cpu)
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
if (tb == NULL) {
uint32_t h;
mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
mmap_unlock();
@ -974,7 +1036,8 @@ int cpu_exec(CPUState *cpu)
* We add the TB in the virtual pc hash table
* for the fast lookup
*/
qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
h = tb_jmp_cache_hash_func(pc);
tb_jmp_cache_set(cpu->tb_jmp_cache, h, tb, pc);
}
#ifndef CONFIG_USER_ONLY
@ -984,7 +1047,7 @@ int cpu_exec(CPUState *cpu)
* direct jump to a TB spanning two pages because the mapping
* for the second page can change.
*/
if (tb->page_addr[1] != -1) {
if (tb_page_addr1(tb) != -1) {
last_tb = NULL;
}
#endif
@ -993,7 +1056,7 @@ int cpu_exec(CPUState *cpu)
tb_add_jump(last_tb, tb_exit, tb);
}
cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit);
cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit);
/* Try to align the host and virtual clocks
if the guest is in advance */
@ -1016,44 +1079,106 @@ void tcg_exec_realizefn(CPUState *cpu, Error **errp)
cc->tcg_ops->initialize();
tcg_target_initialized = true;
}
tlb_init(cpu);
qemu_plugin_vcpu_init_hook(cpu);
cpu->tb_jmp_cache = g_new0(CPUJumpCache, 1);
tlb_init(cpu);
#ifndef CONFIG_USER_ONLY
tcg_iommu_init_notifier_list(cpu);
#endif /* !CONFIG_USER_ONLY */
/* qemu_plugin_vcpu_init_hook delayed until cpu_index assigned. */
}
/* undo the initializations in reverse order */
void tcg_exec_unrealizefn(CPUState *cpu)
{
qemu_plugin_vcpu_exit_hook(cpu);
#ifndef CONFIG_USER_ONLY
tcg_iommu_free_notifier_list(cpu);
#endif /* !CONFIG_USER_ONLY */
qemu_plugin_vcpu_exit_hook(cpu);
tlb_destroy(cpu);
g_free(cpu->tb_jmp_cache);
}
#ifndef CONFIG_USER_ONLY
void dump_drift_info(void)
static void dump_drift_info(GString *buf)
{
if (!icount_enabled()) {
return;
}
qemu_printf("Host - Guest clock %"PRIi64" ms\n",
(cpu_get_clock() - icount_get()) / SCALE_MS);
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
(cpu_get_clock() - icount_get()) / SCALE_MS);
if (icount_align_option) {
qemu_printf("Max guest delay %"PRIi64" ms\n",
-max_delay / SCALE_MS);
qemu_printf("Max guest advance %"PRIi64" ms\n",
max_advance / SCALE_MS);
g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
-max_delay / SCALE_MS);
g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
max_advance / SCALE_MS);
} else {
qemu_printf("Max guest delay NA\n");
qemu_printf("Max guest advance NA\n");
g_string_append_printf(buf, "Max guest delay NA\n");
g_string_append_printf(buf, "Max guest advance NA\n");
}
}
HumanReadableText *qmp_x_query_jit(Error **errp)
{
g_autoptr(GString) buf = g_string_new("");
if (!tcg_enabled()) {
error_setg(errp, "JIT information is only available with accel=tcg");
return NULL;
}
dump_exec_info(buf);
dump_drift_info(buf);
return human_readable_text_from_str(buf);
}
HumanReadableText *qmp_x_query_opcount(Error **errp)
{
g_autoptr(GString) buf = g_string_new("");
if (!tcg_enabled()) {
error_setg(errp, "Opcode count information is only available with accel=tcg");
return NULL;
}
tcg_dump_op_count(buf);
return human_readable_text_from_str(buf);
}
#ifdef CONFIG_PROFILER
int64_t dev_time;
HumanReadableText *qmp_x_query_profile(Error **errp)
{
g_autoptr(GString) buf = g_string_new("");
static int64_t last_cpu_exec_time;
int64_t cpu_exec_time;
int64_t delta;
cpu_exec_time = tcg_cpu_exec_time();
delta = cpu_exec_time - last_cpu_exec_time;
g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n",
dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n",
delta, delta / (double)NANOSECONDS_PER_SECOND);
last_cpu_exec_time = cpu_exec_time;
dev_time = 0;
return human_readable_text_from_str(buf);
}
#else
HumanReadableText *qmp_x_query_profile(Error **errp)
{
error_setg(errp, "Internal profiler not compiled");
return NULL;
}
#endif
#endif /* !CONFIG_USER_ONLY */

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,14 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-machine.h"
#include "exec/exec-all.h"
#include "monitor/monitor.h"
#include "sysemu/tcg.h"
static void hmp_info_jit(Monitor *mon, const QDict *qdict)
{
if (!tcg_enabled()) {
error_report("JIT information is only available with accel=tcg");
return;
}
dump_exec_info();
dump_drift_info();
}
static void hmp_info_opcount(Monitor *mon, const QDict *qdict)
{
dump_opcount_info();
}
static void hmp_tcg_register(void)
{
monitor_register_hmp("jit", true, hmp_info_jit);
monitor_register_hmp("opcount", true, hmp_info_opcount);
monitor_register_hmp_info_hrt("jit", qmp_x_query_jit);
monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount);
}
type_init(hmp_tcg_register);

View File

@ -11,12 +11,112 @@
#include "exec/exec-all.h"
/*
* Access to the various translations structures need to be serialised
* via locks for consistency. In user-mode emulation access to the
* memory related structures are protected with mmap_lock.
* In !user-mode we use per-page locks.
*/
#ifdef CONFIG_SOFTMMU
#define assert_memory_lock()
#else
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
#endif
typedef struct PageDesc {
/* list of TBs intersecting this ram page */
uintptr_t first_tb;
#ifdef CONFIG_USER_ONLY
unsigned long flags;
void *target_data;
#endif
#ifdef CONFIG_SOFTMMU
QemuSpin lock;
#endif
} PageDesc;
/* Size of the L2 (and L3, etc) page tables. */
#define V_L2_BITS 10
#define V_L2_SIZE (1 << V_L2_BITS)
/*
* L1 Mapping properties
*/
extern int v_l1_size;
extern int v_l1_shift;
extern int v_l2_levels;
/*
* The bottom level has pointers to PageDesc, and is indexed by
* anything from 4 to (V_L2_BITS + 3) bits, depending on target page size.
*/
#define V_L1_MIN_BITS 4
#define V_L1_MAX_BITS (V_L2_BITS + 3)
#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS)
extern void *l1_map[V_L1_MAX_SIZE];
PageDesc *page_find_alloc(tb_page_addr_t index, bool alloc);
static inline PageDesc *page_find(tb_page_addr_t index)
{
return page_find_alloc(index, false);
}
/* list iterators for lists of tagged pointers in TranslationBlock */
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \
for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \
tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \
tb = (TranslationBlock *)((uintptr_t)tb & ~1))
#define PAGE_FOR_EACH_TB(pagedesc, tb, n) \
TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next)
#define TB_FOR_EACH_JMP(head_tb, tb, n) \
TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next)
/* In user-mode page locks aren't used; mmap_lock is enough */
#ifdef CONFIG_USER_ONLY
#define assert_page_locked(pd) tcg_debug_assert(have_mmap_lock())
static inline void page_lock(PageDesc *pd) { }
static inline void page_unlock(PageDesc *pd) { }
#else
#ifdef CONFIG_DEBUG_TCG
void do_assert_page_locked(const PageDesc *pd, const char *file, int line);
#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__)
#else
#define assert_page_locked(pd)
#endif
void page_lock(PageDesc *pd);
void page_unlock(PageDesc *pd);
#endif
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG)
void assert_no_pages_locked(void);
#else
static inline void assert_no_pages_locked(void) { }
#endif
TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
int cflags);
void QEMU_NORETURN cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
void page_init(void);
void tb_htable_init(void);
void tb_reset_jump(TranslationBlock *tb, int n);
TranslationBlock *tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
tb_page_addr_t phys_page2);
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc);
void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
uintptr_t host_pc);
/* Return the current PC from CPU, which may be cached in TB. */
static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb)
{
#if TARGET_TB_PCREL
return cpu->cc->get_pc(cpu);
#else
return tb_pc(tb);
#endif
}
#endif /* ACCEL_TCG_INTERNAL_H */

307
accel/tcg/ldst_common.c.inc Normal file
View File

@ -0,0 +1,307 @@
/*
* Routines common to user and system emulation of load/store.
*
* Copyright (c) 2003 Fabrice Bellard
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
return cpu_ldb_mmu(env, addr, oi, ra);
}
int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra);
}
uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx);
return cpu_ldw_be_mmu(env, addr, oi, ra);
}
int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra);
}
uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx);
return cpu_ldl_be_mmu(env, addr, oi, ra);
}
uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx);
return cpu_ldq_be_mmu(env, addr, oi, ra);
}
uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx);
return cpu_ldw_le_mmu(env, addr, oi, ra);
}
int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra);
}
uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx);
return cpu_ldl_le_mmu(env, addr, oi, ra);
}
uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx);
return cpu_ldq_le_mmu(env, addr, oi, ra);
}
void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
cpu_stb_mmu(env, addr, val, oi, ra);
}
void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx);
cpu_stw_be_mmu(env, addr, val, oi, ra);
}
void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx);
cpu_stl_be_mmu(env, addr, val, oi, ra);
}
void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx);
cpu_stq_be_mmu(env, addr, val, oi, ra);
}
void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx);
cpu_stw_le_mmu(env, addr, val, oi, ra);
}
void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx);
cpu_stl_le_mmu(env, addr, val, oi, ra);
}
void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx);
cpu_stq_le_mmu(env, addr, val, oi, ra);
}
/*--------------------------*/
uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_ldub_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return (int8_t)cpu_ldub_data_ra(env, addr, ra);
}
uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_lduw_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return (int16_t)cpu_lduw_be_data_ra(env, addr, ra);
}
uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_ldl_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_ldq_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_lduw_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return (int16_t)cpu_lduw_le_data_ra(env, addr, ra);
}
uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_ldl_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return cpu_ldq_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra);
}
void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
cpu_stb_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
cpu_stw_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
cpu_stl_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr,
uint64_t val, uintptr_t ra)
{
cpu_stq_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
cpu_stw_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
cpu_stl_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr,
uint64_t val, uintptr_t ra)
{
cpu_stq_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra);
}
/*--------------------------*/
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldub_data_ra(env, addr, 0);
}
int cpu_ldsb_data(CPUArchState *env, abi_ptr addr)
{
return (int8_t)cpu_ldub_data(env, addr);
}
uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr addr)
{
return cpu_lduw_be_data_ra(env, addr, 0);
}
int cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr)
{
return (int16_t)cpu_lduw_be_data(env, addr);
}
uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldl_be_data_ra(env, addr, 0);
}
uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldq_be_data_ra(env, addr, 0);
}
uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr addr)
{
return cpu_lduw_le_data_ra(env, addr, 0);
}
int cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr)
{
return (int16_t)cpu_lduw_le_data(env, addr);
}
uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldl_le_data_ra(env, addr, 0);
}
uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldq_le_data_ra(env, addr, 0);
}
void cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stb_data_ra(env, addr, val, 0);
}
void cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stw_be_data_ra(env, addr, val, 0);
}
void cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stl_be_data_ra(env, addr, val, 0);
}
void cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val)
{
cpu_stq_be_data_ra(env, addr, val, 0);
}
void cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stw_le_data_ra(env, addr, val, 0);
}
void cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stl_le_data_ra(env, addr, val, 0);
}
void cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val)
{
cpu_stq_le_data_ra(env, addr, val, 0);
}

View File

@ -3,6 +3,7 @@ tcg_ss.add(files(
'tcg-all.c',
'cpu-exec-common.c',
'cpu-exec.c',
'tb-maint.c',
'tcg-runtime-gvec.c',
'tcg-runtime.c',
'translate-all.c',
@ -10,7 +11,7 @@ tcg_ss.add(files(
))
tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c'))
tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c'), libdl])
tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c')])
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files(

View File

@ -45,7 +45,6 @@
#include "qemu/osdep.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
#include "trace/mem.h"
#include "exec/exec-all.h"
#include "exec/plugin-gen.h"
#include "exec/translator.h"
@ -163,11 +162,7 @@ static void gen_empty_mem_helper(void)
static void gen_plugin_cb_start(enum plugin_gen_from from,
enum plugin_gen_cb type, unsigned wr)
{
TCGOp *op;
tcg_gen_plugin_cb_start(from, type, wr);
op = tcg_last_op();
QSIMPLEQ_INSERT_TAIL(&tcg_ctx->plugin_ops, op, plugin_link);
}
static void gen_wrapped(enum plugin_gen_from from,
@ -211,9 +206,9 @@ static void gen_mem_wrapped(enum plugin_gen_cb type,
const union mem_gen_fn *f, TCGv addr,
uint32_t info, bool is_mem)
{
int wr = !!(info & TRACE_MEM_ST);
enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, wr);
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, rw);
if (is_mem) {
f->mem_fn(addr, info);
} else {
@ -707,62 +702,6 @@ static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb,
inject_mem_disable_helper(insn, begin_op);
}
static void plugin_inject_cb(const struct qemu_plugin_tb *ptb, TCGOp *begin_op,
int insn_idx)
{
enum plugin_gen_from from = begin_op->args[0];
enum plugin_gen_cb type = begin_op->args[1];
switch (from) {
case PLUGIN_GEN_FROM_TB:
switch (type) {
case PLUGIN_GEN_CB_UDATA:
plugin_gen_tb_udata(ptb, begin_op);
return;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_tb_inline(ptb, begin_op);
return;
default:
g_assert_not_reached();
}
case PLUGIN_GEN_FROM_INSN:
switch (type) {
case PLUGIN_GEN_CB_UDATA:
plugin_gen_insn_udata(ptb, begin_op, insn_idx);
return;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_insn_inline(ptb, begin_op, insn_idx);
return;
case PLUGIN_GEN_ENABLE_MEM_HELPER:
plugin_gen_enable_mem_helper(ptb, begin_op, insn_idx);
return;
default:
g_assert_not_reached();
}
case PLUGIN_GEN_FROM_MEM:
switch (type) {
case PLUGIN_GEN_CB_MEM:
plugin_gen_mem_regular(ptb, begin_op, insn_idx);
return;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_mem_inline(ptb, begin_op, insn_idx);
return;
default:
g_assert_not_reached();
}
case PLUGIN_GEN_AFTER_INSN:
switch (type) {
case PLUGIN_GEN_DISABLE_MEM_HELPER:
plugin_gen_disable_mem_helper(ptb, begin_op, insn_idx);
return;
default:
g_assert_not_reached();
}
default:
g_assert_not_reached();
}
}
/* #define DEBUG_PLUGIN_GEN_OPS */
static void pr_ops(void)
{
@ -820,42 +759,129 @@ static void pr_ops(void)
static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
{
TCGOp *op;
int insn_idx;
int insn_idx = -1;
pr_ops();
insn_idx = -1;
QSIMPLEQ_FOREACH(op, &tcg_ctx->plugin_ops, plugin_link) {
enum plugin_gen_from from = op->args[0];
enum plugin_gen_cb type = op->args[1];
tcg_debug_assert(op->opc == INDEX_op_plugin_cb_start);
/* ENABLE_MEM_HELPER is the first callback of an instruction */
if (from == PLUGIN_GEN_FROM_INSN &&
type == PLUGIN_GEN_ENABLE_MEM_HELPER) {
QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
switch (op->opc) {
case INDEX_op_insn_start:
insn_idx++;
break;
case INDEX_op_plugin_cb_start:
{
enum plugin_gen_from from = op->args[0];
enum plugin_gen_cb type = op->args[1];
switch (from) {
case PLUGIN_GEN_FROM_TB:
{
g_assert(insn_idx == -1);
switch (type) {
case PLUGIN_GEN_CB_UDATA:
plugin_gen_tb_udata(plugin_tb, op);
break;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_tb_inline(plugin_tb, op);
break;
default:
g_assert_not_reached();
}
break;
}
case PLUGIN_GEN_FROM_INSN:
{
g_assert(insn_idx >= 0);
switch (type) {
case PLUGIN_GEN_CB_UDATA:
plugin_gen_insn_udata(plugin_tb, op, insn_idx);
break;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_insn_inline(plugin_tb, op, insn_idx);
break;
case PLUGIN_GEN_ENABLE_MEM_HELPER:
plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx);
break;
default:
g_assert_not_reached();
}
break;
}
case PLUGIN_GEN_FROM_MEM:
{
g_assert(insn_idx >= 0);
switch (type) {
case PLUGIN_GEN_CB_MEM:
plugin_gen_mem_regular(plugin_tb, op, insn_idx);
break;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_mem_inline(plugin_tb, op, insn_idx);
break;
default:
g_assert_not_reached();
}
break;
}
case PLUGIN_GEN_AFTER_INSN:
{
g_assert(insn_idx >= 0);
switch (type) {
case PLUGIN_GEN_DISABLE_MEM_HELPER:
plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx);
break;
default:
g_assert_not_reached();
}
break;
}
default:
g_assert_not_reached();
}
break;
}
default:
/* plugins don't care about any other ops */
break;
}
plugin_inject_cb(plugin_tb, op, insn_idx);
}
pr_ops();
}
bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_only)
bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
bool mem_only)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
bool ret = false;
if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) {
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
int i;
/* reset callbacks */
for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
if (ptb->cbs[i]) {
g_array_set_size(ptb->cbs[i], 0);
}
}
ptb->n = 0;
ret = true;
QSIMPLEQ_INIT(&tcg_ctx->plugin_ops);
ptb->vaddr = tb->pc;
ptb->vaddr = db->pc_first;
ptb->vaddr2 = -1;
get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1);
ptb->haddr1 = db->host_addr[0];
ptb->haddr2 = NULL;
ptb->mem_only = mem_only;
plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
}
tcg_ctx->plugin_insn = NULL;
return ret;
}
@ -864,9 +890,8 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
struct qemu_plugin_insn *pinsn;
pinsn = qemu_plugin_tb_insn_get(ptb);
pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next);
tcg_ctx->plugin_insn = pinsn;
pinsn->vaddr = db->pc_next;
plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
/*
@ -874,16 +899,15 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
* Note that we skip this when haddr1 == NULL, e.g. when we're
* fetching instructions from a region not backed by RAM.
*/
if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) &&
unlikely((db->pc_next & TARGET_PAGE_MASK) !=
(db->pc_first & TARGET_PAGE_MASK))) {
get_page_addr_code_hostp(cpu->env_ptr, db->pc_next,
&ptb->haddr2);
ptb->vaddr2 = db->pc_next;
}
if (likely(ptb->vaddr2 == -1)) {
if (ptb->haddr1 == NULL) {
pinsn->haddr = NULL;
} else if (is_same_page(db, db->pc_next)) {
pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
} else {
if (ptb->vaddr2 == -1) {
ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
get_page_addr_code_hostp(cpu->env_ptr, ptb->vaddr2, &ptb->haddr2);
}
pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
}
}
@ -893,23 +917,19 @@ void plugin_gen_insn_end(void)
plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
}
/*
* There are cases where we never get to finalise a translation - for
* example a page fault during translation. As a result we shouldn't
* do any clean-up here and make sure things are reset in
* plugin_gen_tb_start.
*/
void plugin_gen_tb_end(CPUState *cpu)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
int i;
/* collect instrumentation requests */
qemu_plugin_tb_trans_cb(cpu, ptb);
/* inject the instrumentation at the appropriate places */
plugin_gen_inject(ptb);
/* clean up */
for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
if (ptb->cbs[i]) {
g_array_set_size(ptb->cbs[i], 0);
}
}
ptb->n = 0;
tcg_ctx->plugin_insn = NULL;
}

View File

@ -24,6 +24,7 @@
#include "exec/exec-all.h"
#include "qemu/xxhash.h"
#include "qemu/fast-hash.h"
#include "tb-jmp-cache.h"
#ifdef CONFIG_SOFTMMU

65
accel/tcg/tb-jmp-cache.h Normal file
View File

@ -0,0 +1,65 @@
/*
* The per-CPU TranslationBlock jump cache.
*
* Copyright (c) 2003 Fabrice Bellard
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef ACCEL_TCG_TB_JMP_CACHE_H
#define ACCEL_TCG_TB_JMP_CACHE_H
#define TB_JMP_CACHE_BITS 12
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
/*
* Accessed in parallel; all accesses to 'tb' must be atomic.
* For TARGET_TB_PCREL, accesses to 'pc' must be protected by
* a load_acquire/store_release to 'tb'.
*/
struct CPUJumpCache {
struct {
TranslationBlock *tb;
#if TARGET_TB_PCREL
target_ulong pc;
#endif
} array[TB_JMP_CACHE_SIZE];
};
static inline TranslationBlock *
tb_jmp_cache_get_tb(CPUJumpCache *jc, uint32_t hash)
{
#if TARGET_TB_PCREL
/* Use acquire to ensure current load of pc from jc. */
return qatomic_load_acquire(&jc->array[hash].tb);
#else
/* Use rcu_read to ensure current load of pc from *tb. */
return qatomic_rcu_read(&jc->array[hash].tb);
#endif
}
static inline target_ulong
tb_jmp_cache_get_pc(CPUJumpCache *jc, uint32_t hash, TranslationBlock *tb)
{
#if TARGET_TB_PCREL
return jc->array[hash].pc;
#else
return tb_pc(tb);
#endif
}
static inline void
tb_jmp_cache_set(CPUJumpCache *jc, uint32_t hash,
TranslationBlock *tb, target_ulong pc)
{
#if TARGET_TB_PCREL
jc->array[hash].pc = pc;
/* Use store_release on tb to ensure pc is written first. */
qatomic_store_release(&jc->array[hash].tb, tb);
#else
/* Use the pc value already stored in tb->pc. */
qatomic_set(&jc->array[hash].tb, tb);
#endif
}
#endif /* ACCEL_TCG_TB_JMP_CACHE_H */

722
accel/tcg/tb-maint.c Normal file
View File

@ -0,0 +1,722 @@
/*
* Translation Block Maintaince
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "exec/cputlb.h"
#include "exec/log.h"
#include "exec/exec-all.h"
#include "exec/translate-all.h"
#include "sysemu/tcg.h"
#include "tcg/tcg.h"
#include "tb-hash.h"
#include "tb-context.h"
#include "internal.h"
static bool tb_cmp(const void *ap, const void *bp)
{
const TranslationBlock *a = ap;
const TranslationBlock *b = bp;
return ((TARGET_TB_PCREL || tb_pc(a) == tb_pc(b)) &&
a->cs_base == b->cs_base &&
a->flags == b->flags &&
(tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
a->trace_vcpu_dstate == b->trace_vcpu_dstate &&
tb_page_addr0(a) == tb_page_addr0(b) &&
tb_page_addr1(a) == tb_page_addr1(b));
}
static bool inv_tb_cmp(const void *ap, const void *bp)
{
const TranslationBlock *a = ap, *b = bp;
return tb_cmp(ap, bp) && a->ihash == b->ihash;
}
void tb_htable_init(void)
{
unsigned int mode = QHT_MODE_AUTO_RESIZE;
qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode);
qht_init(&tb_ctx.inv_htable, inv_tb_cmp, CODE_GEN_HTABLE_SIZE, mode);
}
/* Set to NULL all the 'first_tb' fields in all PageDescs. */
static void page_flush_tb_1(int level, void **lp)
{
int i;
if (*lp == NULL) {
return;
}
if (level == 0) {
PageDesc *pd = *lp;
for (i = 0; i < V_L2_SIZE; ++i) {
page_lock(&pd[i]);
pd[i].first_tb = (uintptr_t)NULL;
page_unlock(&pd[i]);
}
} else {
void **pp = *lp;
for (i = 0; i < V_L2_SIZE; ++i) {
page_flush_tb_1(level - 1, pp + i);
}
}
}
static void page_flush_tb(void)
{
int i, l1_sz = v_l1_size;
for (i = 0; i < l1_sz; i++) {
page_flush_tb_1(v_l2_levels, l1_map + i);
}
}
/* flush all the translation blocks */
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{
bool did_flush = false;
mmap_lock();
/* If it is already been done on request of another CPU, just retry. */
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
goto done;
}
did_flush = true;
CPU_FOREACH(cpu) {
tcg_flush_jmp_cache(cpu);
}
qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE);
qht_reset_size(&tb_ctx.inv_htable, CODE_GEN_HTABLE_SIZE);
page_flush_tb();
tcg_region_reset_all();
/* XXX: flush processor icache at this point if cache flush is expensive */
qatomic_mb_set(&tb_ctx.tb_flush_count, tb_ctx.tb_flush_count + 1);
done:
mmap_unlock();
if (did_flush) {
qemu_plugin_flush_cb();
}
}
void tb_flush(CPUState *cpu)
{
if (tcg_enabled()) {
unsigned tb_flush_count = qatomic_mb_read(&tb_ctx.tb_flush_count);
if (cpu_in_exclusive_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));
}
}
}
/*
* user-mode: call with mmap_lock held
* !user-mode: call with @pd->lock held
*/
static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb)
{
TranslationBlock *tb1;
uintptr_t *pprev;
unsigned int n1;
assert_page_locked(pd);
pprev = &pd->first_tb;
PAGE_FOR_EACH_TB(pd, tb1, n1) {
if (tb1 == tb) {
*pprev = tb1->page_next[n1];
return;
}
pprev = &tb1->page_next[n1];
}
g_assert_not_reached();
}
/* remove @orig from its @n_orig-th jump list */
static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
{
uintptr_t ptr, ptr_locked;
TranslationBlock *dest;
TranslationBlock *tb;
uintptr_t *pprev;
int n;
/* mark the LSB of jmp_dest[] so that no further jumps can be inserted */
ptr = qatomic_or_fetch(&orig->jmp_dest[n_orig], 1);
dest = (TranslationBlock *)(ptr & ~1);
if (dest == NULL) {
return;
}
qemu_spin_lock(&dest->jmp_lock);
/*
* While acquiring the lock, the jump might have been removed if the
* destination TB was invalidated; check again.
*/
ptr_locked = qatomic_read(&orig->jmp_dest[n_orig]);
if (ptr_locked != ptr) {
qemu_spin_unlock(&dest->jmp_lock);
/*
* The only possibility is that the jump was unlinked via
* tb_jump_unlink(dest). Seeing here another destination would be a bug,
* because we set the LSB above.
*/
g_assert(ptr_locked == 1 && dest->cflags & CF_INVALID);
return;
}
/*
* We first acquired the lock, and since the destination pointer matches,
* we know for sure that @orig is in the jmp list.
*/
pprev = &dest->jmp_list_head;
TB_FOR_EACH_JMP(dest, tb, n) {
if (tb == orig && n == n_orig) {
*pprev = tb->jmp_list_next[n];
/* no need to set orig->jmp_dest[n]; setting the LSB was enough */
qemu_spin_unlock(&dest->jmp_lock);
return;
}
pprev = &tb->jmp_list_next[n];
}
g_assert_not_reached();
}
/*
* Reset the jump entry 'n' of a TB so that it is not chained to another TB.
*/
void tb_reset_jump(TranslationBlock *tb, int n)
{
uintptr_t addr = (uintptr_t)(tb->tc.ptr + tb->jmp_reset_offset[n]);
tb_set_jmp_target(tb, n, addr);
}
/* remove any jumps to the TB */
static inline void tb_jmp_unlink(TranslationBlock *dest)
{
TranslationBlock *tb;
int n;
qemu_spin_lock(&dest->jmp_lock);
TB_FOR_EACH_JMP(dest, tb, n) {
tb_reset_jump(tb, n);
qatomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1);
/* No need to clear the list entry; setting the dest ptr is enough */
}
dest->jmp_list_head = (uintptr_t)NULL;
qemu_spin_unlock(&dest->jmp_lock);
}
static void tb_jmp_cache_inval_tb(TranslationBlock *tb)
{
CPUState *cpu;
if (TARGET_TB_PCREL) {
/* A TB may be at any virtual address */
CPU_FOREACH(cpu) {
tcg_flush_jmp_cache(cpu);
}
} else {
uint32_t h = tb_jmp_cache_hash_func(tb_pc(tb));
CPU_FOREACH(cpu) {
CPUJumpCache *jc = cpu->tb_jmp_cache;
if (qatomic_read(&jc->array[h].tb) == tb) {
qatomic_set(&jc->array[h].tb, NULL);
}
}
}
}
/*
* In user-mode, call with mmap_lock held.
* In !user-mode, if @rm_from_page_list is set, call with the TB's pages'
* locks held.
*/
static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
{
PageDesc *p;
uint32_t h;
tb_page_addr_t phys_pc;
uint32_t orig_cflags = tb_cflags(tb);
void *existing = NULL;
assert_memory_lock();
/* make sure no further incoming jumps will be chained to this TB */
qemu_spin_lock(&tb->jmp_lock);
qatomic_set(&tb->cflags, tb->cflags | CF_INVALID);
qemu_spin_unlock(&tb->jmp_lock);
/* remove the TB from the hash list */
phys_pc = tb_page_addr0(tb);
h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
tb->flags, orig_cflags, tb->trace_vcpu_dstate);
if (!qht_remove(&tb_ctx.htable, tb, h)) {
return;
}
qht_insert(&tb_ctx.inv_htable, tb, h, &existing);
g_assert(existing == NULL);
/* remove the TB from the page list */
if (rm_from_page_list) {
p = page_find(phys_pc >> TARGET_PAGE_BITS);
tb_page_remove(p, tb);
phys_pc = tb_page_addr1(tb);
if (phys_pc != -1) {
p = page_find(phys_pc >> TARGET_PAGE_BITS);
tb_page_remove(p, tb);
}
}
/* remove the TB from the hash list */
tb_jmp_cache_inval_tb(tb);
/* suppress this TB from the two jump lists */
tb_remove_from_jmp_list(tb, 0);
tb_remove_from_jmp_list(tb, 1);
/* suppress any remaining jumps to this TB */
tb_jmp_unlink(tb);
qatomic_set(&tb_ctx.tb_phys_invalidate_count,
tb_ctx.tb_phys_invalidate_count + 1);
}
static void tb_phys_invalidate__locked(TranslationBlock *tb)
{
qemu_thread_jit_write();
do_tb_phys_invalidate(tb, true);
qemu_thread_jit_execute();
}
static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
PageDesc **ret_p2, tb_page_addr_t phys2, bool alloc)
{
PageDesc *p1, *p2;
tb_page_addr_t page1;
tb_page_addr_t page2;
assert_memory_lock();
g_assert(phys1 != -1);
page1 = phys1 >> TARGET_PAGE_BITS;
page2 = phys2 >> TARGET_PAGE_BITS;
p1 = page_find_alloc(page1, alloc);
if (ret_p1) {
*ret_p1 = p1;
}
if (likely(phys2 == -1)) {
page_lock(p1);
return;
} else if (page1 == page2) {
page_lock(p1);
if (ret_p2) {
*ret_p2 = p1;
}
return;
}
p2 = page_find_alloc(page2, alloc);
if (ret_p2) {
*ret_p2 = p2;
}
if (page1 < page2) {
page_lock(p1);
page_lock(p2);
} else {
page_lock(p2);
page_lock(p1);
}
}
#ifdef CONFIG_USER_ONLY
static inline void page_lock_tb(const TranslationBlock *tb) { }
static inline void page_unlock_tb(const TranslationBlock *tb) { }
#else
/* lock the page(s) of a TB in the correct acquisition order */
static void page_lock_tb(const TranslationBlock *tb)
{
page_lock_pair(NULL, tb_page_addr0(tb), NULL, tb_page_addr1(tb), false);
}
static void page_unlock_tb(const TranslationBlock *tb)
{
PageDesc *p1 = page_find(tb_page_addr0(tb) >> TARGET_PAGE_BITS);
page_unlock(p1);
if (unlikely(tb_page_addr1(tb) != -1)) {
PageDesc *p2 = page_find(tb_page_addr1(tb) >> TARGET_PAGE_BITS);
if (p2 != p1) {
page_unlock(p2);
}
}
}
#endif
/*
* Invalidate one TB.
* Called with mmap_lock held in user-mode.
*/
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
{
if (page_addr == -1 && tb_page_addr0(tb) != -1) {
page_lock_tb(tb);
do_tb_phys_invalidate(tb, true);
page_unlock_tb(tb);
} else {
do_tb_phys_invalidate(tb, false);
}
}
/*
* Add the tb in the target page and protect it if necessary.
* Called with mmap_lock held for user-mode emulation.
* Called with @p->lock held in !user-mode.
*/
static inline void tb_page_add(PageDesc *p, TranslationBlock *tb,
unsigned int n, tb_page_addr_t page_addr)
{
#ifndef CONFIG_USER_ONLY
bool page_already_protected;
#endif
assert_page_locked(p);
tb->page_next[n] = p->first_tb;
#ifndef CONFIG_USER_ONLY
page_already_protected = p->first_tb != (uintptr_t)NULL;
#endif
p->first_tb = (uintptr_t)tb | n;
#if defined(CONFIG_USER_ONLY)
/* translator_loop() must have made all TB pages non-writable */
assert(!(p->flags & PAGE_WRITE));
#else
/*
* If some code is already present, then the pages are already
* protected. So we handle the case where only the first TB is
* allocated in a physical page.
*/
if (!page_already_protected) {
tlb_protect_code(page_addr);
}
#endif
}
/*
* Add a new TB and link it to the physical page tables. phys_page2 is
* (-1) to indicate that only one page contains the TB.
*
* Called with mmap_lock held for user-mode emulation.
*
* Returns a pointer @tb, or a pointer to an existing TB that matches @tb.
* Note that in !user-mode, another thread might have already added a TB
* for the same block of guest code that @tb corresponds to. In that case,
* the caller should discard the original @tb, and use instead the returned TB.
*/
TranslationBlock *tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
tb_page_addr_t phys_page2)
{
PageDesc *p;
PageDesc *p2 = NULL;
void *existing_tb = NULL;
uint32_t h;
assert_memory_lock();
tcg_debug_assert(!(tb->cflags & CF_INVALID));
/*
* Add the TB to the page list, acquiring first the pages's locks.
* We keep the locks held until after inserting the TB in the hash table,
* so that if the insertion fails we know for sure that the TBs are still
* in the page descriptors.
* Note that inserting into the hash table first isn't an option, since
* we can only insert TBs that are fully initialized.
*/
page_lock_pair(&p, phys_pc, &p2, phys_page2, true);
tb_page_add(p, tb, 0, phys_pc);
if (p2) {
tb_page_add(p2, tb, 1, phys_page2);
}
/* add in the hash table */
h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
tb->flags, tb->cflags, tb->trace_vcpu_dstate);
qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
/* remove TB from the page(s) if we couldn't insert it */
if (unlikely(existing_tb)) {
tb_page_remove(p, tb);
if (p2) {
tb_page_remove(p2, tb);
}
tb = existing_tb;
}
if (p2 && p2 != p) {
page_unlock(p2);
}
page_unlock(p);
return tb;
}
/*
* @p must be non-NULL.
* user-mode: call with mmap_lock held.
* !user-mode: call with all @pages locked.
*/
static void
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
PageDesc *p, tb_page_addr_t start,
tb_page_addr_t end,
uintptr_t retaddr)
{
TranslationBlock *tb;
#ifndef XBOX
tb_page_addr_t tb_start, tb_end;
#endif
int n;
#ifdef TARGET_HAS_PRECISE_SMC
CPUState *cpu = current_cpu;
bool current_tb_not_found = retaddr != 0;
bool current_tb_modified = false;
TranslationBlock *current_tb = NULL;
#endif /* TARGET_HAS_PRECISE_SMC */
assert_page_locked(p);
/*
* We remove all the TBs in the range [start, end[.
* XXX: see if in some cases it could be faster to invalidate all the code
*/
PAGE_FOR_EACH_TB(p, tb, n) {
assert_page_locked(p);
#ifndef XBOX
/* NOTE: this is subtle as a TB may span two physical pages */
if (n == 0) {
/* NOTE: tb_end may be after the end of the page, but
it is not a problem */
tb_start = tb_page_addr0(tb);
tb_end = tb_start + tb->size;
} else {
tb_start = tb_page_addr1(tb);
tb_end = tb_start + ((tb_page_addr0(tb) + tb->size)
& ~TARGET_PAGE_MASK);
}
if (!(tb_end <= start || tb_start >= end)) {
#else
{
#endif
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_not_found) {
current_tb_not_found = false;
/* now we have a real cpu fault */
current_tb = tcg_tb_lookup(retaddr);
}
if (current_tb == tb &&
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
/*
* If we are modifying the current TB, we must stop
* its execution. We could be more precise by checking
* that the modification is after the current PC, but it
* would require a specialized function to partially
* restore the CPU state.
*/
current_tb_modified = true;
cpu_restore_state_from_tb(cpu, current_tb, retaddr);
}
#endif /* TARGET_HAS_PRECISE_SMC */
tb_phys_invalidate__locked(tb);
}
}
#if !defined(CONFIG_USER_ONLY)
/* if no code remaining, no need to continue to use slow writes */
if (!p->first_tb) {
tlb_unprotect_code(start);
}
#endif
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_modified) {
page_collection_unlock(pages);
/* Force execution of one insn next time. */
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
mmap_unlock();
cpu_loop_exit_noexc(cpu);
}
#endif
}
/*
* Invalidate all TBs which intersect with the target physical
* address page @addr.
*
* Called with mmap_lock held for user-mode emulation
*/
void tb_invalidate_phys_page(tb_page_addr_t addr)
{
struct page_collection *pages;
tb_page_addr_t start, end;
PageDesc *p;
assert_memory_lock();
p = page_find(addr >> TARGET_PAGE_BITS);
if (p == NULL) {
return;
}
start = addr & TARGET_PAGE_MASK;
end = start + TARGET_PAGE_SIZE;
pages = page_collection_lock(start, end);
tb_invalidate_phys_page_range__locked(pages, p, start, end, 0);
page_collection_unlock(pages);
}
/*
* Invalidate all TBs which intersect with the target physical address range
* [start;end[. NOTE: start and end may refer to *different* physical pages.
* 'is_cpu_write_access' should be true if called from a real cpu write
* access: the virtual CPU will exit the current TB if code is modified inside
* this TB.
*
* Called with mmap_lock held for user-mode emulation.
*/
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
{
struct page_collection *pages;
tb_page_addr_t next;
assert_memory_lock();
pages = page_collection_lock(start, end);
for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
start < end;
start = next, next += TARGET_PAGE_SIZE) {
PageDesc *pd = page_find(start >> TARGET_PAGE_BITS);
tb_page_addr_t bound = MIN(next, end);
if (pd == NULL) {
continue;
}
tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0);
}
page_collection_unlock(pages);
}
#ifdef CONFIG_SOFTMMU
/*
* len must be <= 8 and start must be a multiple of len.
* Called via softmmu_template.h when code areas are written to with
* iothread mutex not held.
*
* Call with all @pages in the range [@start, @start + len[ locked.
*/
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len,
uintptr_t retaddr)
{
PageDesc *p;
assert_memory_lock();
p = page_find(start >> TARGET_PAGE_BITS);
if (!p) {
return;
}
assert_page_locked(p);
tb_invalidate_phys_page_range__locked(pages, p, start, start + len,
retaddr);
}
#else
/*
* Called with mmap_lock held. If pc is not 0 then it indicates the
* host PC of the faulting store instruction that caused this invalidate.
* Returns true if the caller needs to abort execution of the current
* TB (because it was modified by this store and the guest CPU has
* precise-SMC semantics).
*/
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
{
TranslationBlock *tb;
PageDesc *p;
int n;
#ifdef TARGET_HAS_PRECISE_SMC
TranslationBlock *current_tb = NULL;
CPUState *cpu = current_cpu;
bool current_tb_modified = false;
#endif
assert_memory_lock();
addr &= TARGET_PAGE_MASK;
p = page_find(addr >> TARGET_PAGE_BITS);
if (!p) {
return false;
}
#ifdef TARGET_HAS_PRECISE_SMC
if (p->first_tb && pc != 0) {
current_tb = tcg_tb_lookup(pc);
}
#endif
assert_page_locked(p);
PAGE_FOR_EACH_TB(p, tb, n) {
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb == tb &&
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
/*
* If we are modifying the current TB, we must stop its execution.
* We could be more precise by checking that the modification is
* after the current PC, but it would require a specialized
* function to partially restore the CPU state.
*/
current_tb_modified = true;
cpu_restore_state_from_tb(cpu, current_tb, pc);
}
#endif /* TARGET_HAS_PRECISE_SMC */
tb_phys_invalidate(tb, addr);
}
p->first_tb = (uintptr_t)NULL;
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_modified) {
/* Force execution of one insn next time. */
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
return true;
}
#endif
return false;
}
#endif

View File

@ -24,9 +24,8 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "sysemu/replay.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#include "exec/exec-all.h"
@ -85,8 +84,7 @@ void icount_handle_deadline(void)
* Don't interrupt cpu thread, when these events are waiting
* (i.e., there is no checkpoint)
*/
if (deadline == 0
&& (replay_mode != REPLAY_MODE_PLAY || replay_has_checkpoint())) {
if (deadline == 0) {
icount_notify_aio_contexts();
}
}
@ -110,8 +108,14 @@ void icount_prepare_for_run(CPUState *cpu)
replay_mutex_lock();
if (cpu->icount_budget == 0 && replay_has_checkpoint()) {
if (cpu->icount_budget == 0) {
/*
* We're called without the iothread lock, so must take it while
* we're calling timer handlers.
*/
qemu_mutex_lock_iothread();
icount_notify_aio_contexts();
qemu_mutex_unlock_iothread();
}
}

View File

@ -7,8 +7,8 @@
* See the COPYING file in the top-level directory.
*/
#ifndef TCG_CPUS_ICOUNT_H
#define TCG_CPUS_ICOUNT_H
#ifndef TCG_ACCEL_OPS_ICOUNT_H
#define TCG_ACCEL_OPS_ICOUNT_H
void icount_handle_deadline(void);
void icount_prepare_for_run(CPUState *cpu);
@ -16,4 +16,4 @@ void icount_process_data(CPUState *cpu);
void icount_handle_interrupt(CPUState *cpu, int mask);
#endif /* TCG_CPUS_ICOUNT_H */
#endif /* TCG_ACCEL_OPS_ICOUNT_H */

View File

@ -24,10 +24,11 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "sysemu/replay.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/notify.h"
#include "qemu/guest-random.h"
#include "exec/exec-all.h"
#include "hw/boards.h"
@ -35,6 +36,26 @@
#include "tcg-accel-ops.h"
#include "tcg-accel-ops-mttcg.h"
typedef struct MttcgForceRcuNotifier {
Notifier notifier;
CPUState *cpu;
} MttcgForceRcuNotifier;
static void do_nothing(CPUState *cpu, run_on_cpu_data d)
{
}
static void mttcg_force_rcu(Notifier *notify, void *data)
{
CPUState *cpu = container_of(notify, MttcgForceRcuNotifier, notifier)->cpu;
/*
* Called with rcu_registry_lock held, using async_run_on_cpu() ensures
* that there are no deadlocks.
*/
async_run_on_cpu(cpu, do_nothing, RUN_ON_CPU_NULL);
}
/*
* In the multi-threaded case each vCPU has its own thread. The TLS
* variable current_cpu can be used deep in the code to find the
@ -43,12 +64,16 @@
static void *mttcg_cpu_thread_fn(void *arg)
{
MttcgForceRcuNotifier force_rcu;
CPUState *cpu = arg;
assert(tcg_enabled());
g_assert(!icount_enabled());
rcu_register_thread();
force_rcu.notifier.notify = mttcg_force_rcu;
force_rcu.cpu = cpu;
rcu_add_force_rcu_notifier(&force_rcu.notifier);
tcg_register_thread();
qemu_mutex_lock_iothread();
@ -100,6 +125,7 @@ static void *mttcg_cpu_thread_fn(void *arg)
tcg_cpus_destroy(cpu);
qemu_mutex_unlock_iothread();
rcu_remove_force_rcu_notifier(&force_rcu.notifier);
rcu_unregister_thread();
return NULL;
}
@ -116,7 +142,7 @@ void mttcg_start_vcpu_thread(CPUState *cpu)
g_assert(tcg_enabled());
tcg_cpu_init_cflags(cpu, current_machine->smp.max_cpus > 1);
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->thread = g_new0(QemuThread, 1);
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);

View File

@ -7,8 +7,8 @@
* See the COPYING file in the top-level directory.
*/
#ifndef TCG_CPUS_MTTCG_H
#define TCG_CPUS_MTTCG_H
#ifndef TCG_ACCEL_OPS_MTTCG_H
#define TCG_ACCEL_OPS_MTTCG_H
/* kick MTTCG vCPU thread */
void mttcg_kick_vcpu_thread(CPUState *cpu);
@ -16,4 +16,4 @@ void mttcg_kick_vcpu_thread(CPUState *cpu);
/* start an mttcg vCPU thread */
void mttcg_start_vcpu_thread(CPUState *cpu);
#endif /* TCG_CPUS_MTTCG_H */
#endif /* TCG_ACCEL_OPS_MTTCG_H */

View File

@ -24,10 +24,11 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "sysemu/replay.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/notify.h"
#include "qemu/guest-random.h"
#include "exec/exec-all.h"
@ -50,7 +51,7 @@ void rr_kick_vcpu_thread(CPUState *unused)
*
* The kick timer is responsible for moving single threaded vCPU
* emulation on to the next vCPU. If more than one vCPU is running a
* timer event with force a cpu->exit so the next vCPU can get
* timer event we force a cpu->exit so the next vCPU can get
* scheduled.
*
* The timer is removed if all vCPUs are idle and restarted again once
@ -60,8 +61,6 @@ void rr_kick_vcpu_thread(CPUState *unused)
static QEMUTimer *rr_kick_vcpu_timer;
static CPUState *rr_current_cpu;
#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10)
static inline int64_t rr_next_kick_time(void)
{
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD;
@ -135,6 +134,11 @@ static void rr_deal_with_unplugged_cpus(void)
}
}
static void rr_force_rcu(Notifier *notify, void *data)
{
rr_kick_next_cpu();
}
/*
* In the single-threaded case each vCPU is simulated in turn. If
* there is more than a single vCPU we create a simple timer to kick
@ -145,10 +149,13 @@ static void rr_deal_with_unplugged_cpus(void)
static void *rr_cpu_thread_fn(void *arg)
{
Notifier force_rcu;
CPUState *cpu = arg;
assert(tcg_enabled());
rcu_register_thread();
force_rcu.notify = rr_force_rcu;
rcu_add_force_rcu_notifier(&force_rcu);
tcg_register_thread();
qemu_mutex_lock_iothread();
@ -257,6 +264,7 @@ static void *rr_cpu_thread_fn(void *arg)
rr_deal_with_unplugged_cpus();
}
rcu_remove_force_rcu_notifier(&force_rcu);
rcu_unregister_thread();
return NULL;
}
@ -271,8 +279,8 @@ void rr_start_vcpu_thread(CPUState *cpu)
tcg_cpu_init_cflags(cpu, false);
if (!single_tcg_cpu_thread) {
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
cpu->thread = g_new0(QemuThread, 1);
cpu->halt_cond = g_new0(QemuCond, 1);
qemu_cond_init(cpu->halt_cond);
/* share a single thread for all cpus with TCG */

View File

@ -7,8 +7,8 @@
* See the COPYING file in the top-level directory.
*/
#ifndef TCG_CPUS_RR_H
#define TCG_CPUS_RR_H
#ifndef TCG_ACCEL_OPS_RR_H
#define TCG_ACCEL_OPS_RR_H
#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10)
@ -18,4 +18,4 @@ void rr_kick_vcpu_thread(CPUState *unused);
/* start the round robin vcpu thread */
void rr_start_vcpu_thread(CPUState *cpu);
#endif /* TCG_CPUS_RR_H */
#endif /* TCG_ACCEL_OPS_RR_H */

View File

@ -26,12 +26,14 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "sysemu/replay.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#include "exec/exec-all.h"
#include "exec/hwaddr.h"
#include "exec/gdbstub.h"
#include "tcg-accel-ops.h"
#include "tcg-accel-ops-mttcg.h"
@ -91,23 +93,120 @@ void tcg_handle_interrupt(CPUState *cpu, int mask)
}
}
static bool tcg_supports_guest_debug(void)
{
return true;
}
/* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */
static inline int xlat_gdb_type(CPUState *cpu, int gdbtype)
{
static const int xlat[] = {
[GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE,
[GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ,
[GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS,
};
CPUClass *cc = CPU_GET_CLASS(cpu);
int cputype = xlat[gdbtype];
if (cc->gdb_stop_before_watchpoint) {
cputype |= BP_STOP_BEFORE_ACCESS;
}
return cputype;
}
static int tcg_insert_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len)
{
CPUState *cpu;
int err = 0;
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
CPU_FOREACH(cpu) {
err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL);
if (err) {
break;
}
}
return err;
case GDB_WATCHPOINT_WRITE:
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
CPU_FOREACH(cpu) {
err = cpu_watchpoint_insert(cpu, addr, len,
xlat_gdb_type(cpu, type), NULL);
if (err) {
break;
}
}
return err;
default:
return -ENOSYS;
}
}
static int tcg_remove_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len)
{
CPUState *cpu;
int err = 0;
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
CPU_FOREACH(cpu) {
err = cpu_breakpoint_remove(cpu, addr, BP_GDB);
if (err) {
break;
}
}
return err;
case GDB_WATCHPOINT_WRITE:
case GDB_WATCHPOINT_READ:
case GDB_WATCHPOINT_ACCESS:
CPU_FOREACH(cpu) {
err = cpu_watchpoint_remove(cpu, addr, len,
xlat_gdb_type(cpu, type));
if (err) {
break;
}
}
return err;
default:
return -ENOSYS;
}
}
static inline void tcg_remove_all_breakpoints(CPUState *cpu)
{
cpu_breakpoint_remove_all(cpu, BP_GDB);
cpu_watchpoint_remove_all(cpu, BP_GDB);
}
static void tcg_accel_ops_init(AccelOpsClass *ops)
{
if (qemu_tcg_mttcg_enabled()) {
ops->create_vcpu_thread = mttcg_start_vcpu_thread;
ops->kick_vcpu_thread = mttcg_kick_vcpu_thread;
ops->handle_interrupt = tcg_handle_interrupt;
} else if (icount_enabled()) {
ops->create_vcpu_thread = rr_start_vcpu_thread;
ops->kick_vcpu_thread = rr_kick_vcpu_thread;
ops->handle_interrupt = icount_handle_interrupt;
ops->get_virtual_clock = icount_get;
ops->get_elapsed_ticks = icount_get;
} else {
ops->create_vcpu_thread = rr_start_vcpu_thread;
ops->kick_vcpu_thread = rr_kick_vcpu_thread;
ops->handle_interrupt = tcg_handle_interrupt;
if (icount_enabled()) {
ops->handle_interrupt = icount_handle_interrupt;
ops->get_virtual_clock = icount_get;
ops->get_elapsed_ticks = icount_get;
} else {
ops->handle_interrupt = tcg_handle_interrupt;
}
}
ops->supports_guest_debug = tcg_supports_guest_debug;
ops->insert_breakpoint = tcg_insert_breakpoint;
ops->remove_breakpoint = tcg_remove_breakpoint;
ops->remove_all_breakpoints = tcg_remove_all_breakpoints;
}
static void tcg_accel_ops_class_init(ObjectClass *oc, void *data)

View File

@ -9,8 +9,8 @@
* See the COPYING file in the top-level directory.
*/
#ifndef TCG_CPUS_H
#define TCG_CPUS_H
#ifndef TCG_ACCEL_OPS_H
#define TCG_ACCEL_OPS_H
#include "sysemu/cpus.h"
@ -19,4 +19,4 @@ int tcg_cpus_exec(CPUState *cpu);
void tcg_handle_interrupt(CPUState *cpu, int mask);
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
#endif /* TCG_CPUS_H */
#endif /* TCG_ACCEL_OPS_H */

View File

@ -24,8 +24,8 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "sysemu/replay.h"
#include "sysemu/cpu-timers.h"
#include "tcg/tcg.h"
#include "qapi/error.h"
@ -208,12 +208,28 @@ static void tcg_set_splitwx(Object *obj, bool value, Error **errp)
s->splitwx_enabled = value;
}
static int tcg_gdbstub_supported_sstep_flags(void)
{
/*
* In replay mode all events will come from the log and can't be
* suppressed otherwise we would break determinism. However as those
* events are tied to the number of executed instructions we won't see
* them occurring every time we single step.
*/
if (replay_mode != REPLAY_MODE_NONE) {
return SSTEP_ENABLE;
} else {
return SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER;
}
}
static void tcg_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg";
ac->init_machine = tcg_init_machine;
ac->allowed = &tcg_allowed;
ac->gdbstub_supported_sstep_flags = tcg_gdbstub_supported_sstep_flags;
object_class_property_add_str(oc, "thread",
tcg_get_thread,

File diff suppressed because it is too large Load Diff

View File

@ -43,20 +43,27 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
}
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
CPUState *cpu, TranslationBlock *tb, int max_insns)
void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns,
target_ulong pc, void *host_pc,
const TranslatorOps *ops, DisasContextBase *db)
{
uint32_t cflags = tb_cflags(tb);
bool plugin_enabled;
/* Initialize DisasContext */
db->tb = tb;
db->pc_first = tb->pc;
db->pc_next = db->pc_first;
db->pc_first = pc;
db->pc_next = pc;
db->is_jmp = DISAS_NEXT;
db->num_insns = 0;
db->max_insns = max_insns;
db->singlestep_enabled = cflags & CF_SINGLE_STEP;
db->host_addr[0] = host_pc;
db->host_addr[1] = NULL;
#ifdef CONFIG_USER_ONLY
page_protect(pc);
#endif
ops->init_disas_context(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
@ -69,7 +76,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->tb_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY);
plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
while (true) {
db->num_insns++;
@ -126,16 +133,120 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* The disas_log hook may use these values rather than recompute. */
tb->size = db->pc_next - db->pc_first;
tb->icount = db->num_insns;
tb->ihash = tb_code_hash_func(cpu->env_ptr, tb->pc, tb->size);
tb->ihash = tb_code_hash_func(cpu->env_ptr, db->pc_first, tb->size);
#ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
&& qemu_log_in_addr_range(db->pc_first)) {
FILE *logfile = qemu_log_lock();
qemu_log("----------------\n");
ops->disas_log(db, cpu);
qemu_log("\n");
qemu_log_unlock(logfile);
FILE *logfile = qemu_log_trylock();
if (logfile) {
fprintf(logfile, "----------------\n");
ops->disas_log(db, cpu, logfile);
fprintf(logfile, "\n");
qemu_log_unlock(logfile);
}
}
#endif
}
static void *translator_access(CPUArchState *env, DisasContextBase *db,
target_ulong pc, size_t len)
{
void *host;
target_ulong base, end;
TranslationBlock *tb;
tb = db->tb;
/* Use slow path if first page is MMIO. */
if (unlikely(tb_page_addr0(tb) == -1)) {
return NULL;
}
end = pc + len - 1;
if (likely(is_same_page(db, end))) {
host = db->host_addr[0];
base = db->pc_first;
} else {
host = db->host_addr[1];
base = TARGET_PAGE_ALIGN(db->pc_first);
if (host == NULL) {
tb_page_addr_t phys_page =
get_page_addr_code_hostp(env, base, &db->host_addr[1]);
/* We cannot handle MMIO as second page. */
assert(phys_page != -1);
tb_set_page_addr1(tb, phys_page);
#ifdef CONFIG_USER_ONLY
page_protect(end);
#endif
host = db->host_addr[1];
}
/* Use slow path when crossing pages. */
if (is_same_page(db, pc)) {
return NULL;
}
}
tcg_debug_assert(pc >= base);
return host + (pc - base);
}
uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
{
uint8_t ret;
void *p = translator_access(env, db, pc, sizeof(ret));
if (p) {
plugin_insn_append(pc, p, sizeof(ret));
return ldub_p(p);
}
ret = cpu_ldub_code(env, pc);
plugin_insn_append(pc, &ret, sizeof(ret));
return ret;
}
uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
{
uint16_t ret, plug;
void *p = translator_access(env, db, pc, sizeof(ret));
if (p) {
plugin_insn_append(pc, p, sizeof(ret));
return lduw_p(p);
}
ret = cpu_lduw_code(env, pc);
plug = tswap16(ret);
plugin_insn_append(pc, &plug, sizeof(ret));
return ret;
}
uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
{
uint32_t ret, plug;
void *p = translator_access(env, db, pc, sizeof(ret));
if (p) {
plugin_insn_append(pc, p, sizeof(ret));
return ldl_p(p);
}
ret = cpu_ldl_code(env, pc);
plug = tswap32(ret);
plugin_insn_append(pc, &plug, sizeof(ret));
return ret;
}
uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
{
uint64_t ret, plug;
void *p = translator_access(env, db, pc, sizeof(ret));
if (p) {
plugin_insn_append(pc, p, sizeof(ret));
return ldq_p(p);
}
ret = cpu_ldq_code(env, pc);
plug = tswap64(ret);
plugin_insn_append(pc, &plug, sizeof(ret));
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,7 @@ struct alsa_params_obt {
snd_pcm_uframes_t samples;
};
static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
static void G_GNUC_PRINTF (2, 3) alsa_logerr (int err, const char *fmt, ...)
{
va_list ap;
@ -83,7 +83,7 @@ static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
}
static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
static void G_GNUC_PRINTF (3, 4) alsa_logerr2 (
int err,
const char *typ,
const char *fmt,
@ -602,6 +602,42 @@ static int alsa_open(bool in, struct alsa_params_req *req,
return -1;
}
static size_t alsa_buffer_get_free(HWVoiceOut *hw)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *)hw;
snd_pcm_sframes_t avail;
size_t alsa_free, generic_free, generic_in_use;
avail = snd_pcm_avail_update(alsa->handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover(alsa->handle)) {
avail = snd_pcm_avail_update(alsa->handle);
}
}
if (avail < 0) {
alsa_logerr(avail,
"Could not obtain number of available frames\n");
avail = 0;
}
}
alsa_free = avail * hw->info.bytes_per_frame;
generic_free = audio_generic_buffer_get_free(hw);
generic_in_use = hw->samples * hw->info.bytes_per_frame - generic_free;
if (generic_in_use) {
/*
* This code can only be reached in the unlikely case that
* snd_pcm_avail_update() returned a larger number of frames
* than snd_pcm_writei() could write. Make sure that all
* remaining bytes in the generic buffer can be written.
*/
alsa_free = alsa_free > generic_in_use ? alsa_free - generic_in_use : 0;
}
return alsa_free;
}
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
@ -916,6 +952,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
.write = alsa_write,
.buffer_get_free = alsa_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = alsa_enable_out,

View File

@ -32,7 +32,8 @@
#include "qapi/qapi-visit-audio.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "qemu-common.h"
#include "qemu/help_option.h"
#include "sysemu/sysemu.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "ui/qemu-spice.h"
@ -72,20 +73,24 @@ void audio_driver_register(audio_driver *drv)
audio_driver *audio_driver_lookup(const char *name)
{
struct audio_driver *d;
Error *local_err = NULL;
int rv;
QLIST_FOREACH(d, &audio_drivers, next) {
if (strcmp(name, d->name) == 0) {
return d;
}
}
audio_module_load_one(name);
QLIST_FOREACH(d, &audio_drivers, next) {
if (strcmp(name, d->name) == 0) {
return d;
rv = audio_module_load(name, &local_err);
if (rv > 0) {
QLIST_FOREACH(d, &audio_drivers, next) {
if (strcmp(name, d->name) == 0) {
return d;
}
}
} else if (rv < 0) {
error_report_err(local_err);
}
return NULL;
}
@ -117,7 +122,6 @@ int audio_bug (const char *funcname, int cond)
AUD_log (NULL, "I am sorry\n");
}
AUD_log (NULL, "Context:\n");
abort();
}
return cond;
@ -548,65 +552,45 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
return live;
}
static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
{
size_t clipped = 0;
size_t pos = hw->mix_buf->pos;
size_t conv = 0;
STSampleBuffer *conv_buf = hw->conv_buf;
while (len) {
st_sample *src = hw->mix_buf->samples + pos;
uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
while (samples) {
uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);
size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);
hw->clip(dst, src, samples_to_clip);
pos = (pos + samples_to_clip) % hw->mix_buf->size;
len -= samples_to_clip;
clipped += samples_to_clip;
hw->conv(conv_buf->samples + conv_buf->pos, src, proc);
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
samples -= proc;
conv += proc;
}
return conv;
}
/*
* Soft voice (capture)
*/
static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
{
HWVoiceIn *hw = sw->hw;
ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
ssize_t rpos;
if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) {
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
return 0;
}
rpos = hw->conv_buf->pos - live;
if (rpos >= 0) {
return rpos;
} else {
return hw->conv_buf->size + rpos;
}
}
static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
{
HWVoiceIn *hw = sw->hw;
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
struct st_sample *src, *dst = sw->buf;
rpos = audio_pcm_sw_get_rpos_in(sw) % hw->conv_buf->size;
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
if (!live) {
return 0;
}
if (audio_bug(__func__, live > hw->conv_buf->size)) {
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
return 0;
}
rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
samples = size / sw->info.bytes_per_frame;
if (!live) {
return 0;
}
swlim = (live * sw->ratio) >> 32;
swlim = MIN (swlim, samples);
@ -632,7 +616,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
total += isamp;
}
if (hw->pcm_ops && !hw->pcm_ops->volume_in) {
if (!hw->pcm_ops->volume_in) {
mixeng_volume (sw->buf, ret, &sw->vol);
}
@ -683,12 +667,38 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
return 0;
}
static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)
{
return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :
INT_MAX) / hw->info.bytes_per_frame;
}
static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
{
size_t clipped = 0;
size_t pos = hw->mix_buf->pos;
while (len) {
st_sample *src = hw->mix_buf->samples + pos;
uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
hw->clip(dst, src, samples_to_clip);
pos = (pos + samples_to_clip) % hw->mix_buf->size;
len -= samples_to_clip;
clipped += samples_to_clip;
}
}
/*
* Soft voice (playback)
*/
static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
{
size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, blck;
size_t hw_free;
size_t ret = 0, pos = 0, total = 0;
if (!sw) {
@ -711,27 +721,28 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
}
wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
samples = size / sw->info.bytes_per_frame;
dead = hwsamples - live;
swlim = ((int64_t) dead << 32) / sw->ratio;
swlim = MIN (swlim, samples);
if (swlim) {
sw->conv (sw->buf, buf, swlim);
hw_free = audio_pcm_hw_get_free(sw->hw);
hw_free = hw_free > live ? hw_free - live : 0;
samples = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio;
samples = MIN(samples, size / sw->info.bytes_per_frame);
if (samples) {
sw->conv(sw->buf, buf, samples);
if (sw->hw->pcm_ops && !sw->hw->pcm_ops->volume_out) {
mixeng_volume (sw->buf, swlim, &sw->vol);
if (!sw->hw->pcm_ops->volume_out) {
mixeng_volume(sw->buf, samples, &sw->vol);
}
}
while (swlim) {
while (samples) {
dead = hwsamples - live;
left = hwsamples - wpos;
blck = MIN (dead, left);
if (!blck) {
break;
}
isamp = swlim;
isamp = samples;
osamp = blck;
st_rate_flow_mix (
sw->rate,
@ -741,7 +752,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
&osamp
);
ret += isamp;
swlim -= isamp;
samples -= isamp;
pos += isamp;
live += osamp;
wpos = (wpos + osamp) % hwsamples;
@ -979,6 +990,18 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
}
}
/**
* audio_frontend_frames_in() - returns the number of frames the resampling
* code generates from frames_in frames
*
* @sw: audio recording frontend
* @frames_in: number of frames
*/
static size_t audio_frontend_frames_in(SWVoiceIn *sw, size_t frames_in)
{
return (int64_t)frames_in * sw->ratio >> 32;
}
static size_t audio_get_avail (SWVoiceIn *sw)
{
size_t live;
@ -995,12 +1018,24 @@ static size_t audio_get_avail (SWVoiceIn *sw)
}
ldebug (
"%s: get_avail live %zu ret %" PRId64 "\n",
"%s: get_avail live %zu frontend frames %zu\n",
SW_NAME (sw),
live, (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame
live, audio_frontend_frames_in(sw, live)
);
return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame;
return live;
}
/**
* audio_frontend_frames_out() - returns the number of frames needed to
* get frames_out frames after resampling
*
* @sw: audio playback frontend
* @frames_out: number of frames
*/
static size_t audio_frontend_frames_out(SWVoiceOut *sw, size_t frames_out)
{
return ((int64_t)frames_out << 32) / sw->ratio;
}
static size_t audio_get_free(SWVoiceOut *sw)
@ -1022,13 +1057,11 @@ static size_t audio_get_free(SWVoiceOut *sw)
dead = sw->hw->mix_buf->size - live;
#ifdef DEBUG_OUT
dolog ("%s: get_free live %zu dead %zu ret %" PRId64 "\n",
SW_NAME (sw),
live, dead, (((int64_t) dead << 32) / sw->ratio) *
sw->info.bytes_per_frame);
dolog("%s: get_free live %zu dead %zu frontend frames %zu\n",
SW_NAME(sw), live, dead, audio_frontend_frames_out(sw, dead));
#endif
return (((int64_t) dead << 32) / sw->ratio) * sw->info.bytes_per_frame;
return dead;
}
static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
@ -1111,8 +1144,12 @@ static void audio_run_out (AudioState *s)
HWVoiceOut *hw = NULL;
SWVoiceOut *sw;
if (!audio_get_pdo_out(s->dev)->mixing_engine) {
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
size_t played, live, prev_rpos;
size_t hw_free = audio_pcm_hw_get_free(hw);
int nb_live;
if (!audio_get_pdo_out(s->dev)->mixing_engine) {
/* there is exactly 1 sw for each hw with no mixeng */
sw = hw->sw_head.lh_first;
@ -1125,15 +1162,34 @@ static void audio_run_out (AudioState *s)
}
if (sw->active) {
sw->callback.fn(sw->callback.opaque, INT_MAX);
sw->callback.fn(sw->callback.opaque,
hw_free * sw->info.bytes_per_frame);
}
if (hw->pcm_ops->run_buffer_out) {
hw->pcm_ops->run_buffer_out(hw);
}
continue;
}
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) {
size_t sw_free = audio_get_free(sw);
size_t free;
if (hw_free > sw->total_hw_samples_mixed) {
free = audio_frontend_frames_out(sw,
MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
} else {
free = 0;
}
if (free > 0) {
sw->callback.fn(sw->callback.opaque,
free * sw->info.bytes_per_frame);
}
}
}
return;
}
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
size_t played, live, prev_rpos, free;
int nb_live;
live = audio_pcm_hw_get_live_out (hw, &nb_live);
if (!nb_live) {
@ -1163,14 +1219,6 @@ static void audio_run_out (AudioState *s)
}
if (!live) {
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) {
free = audio_get_free (sw);
if (free > 0) {
sw->callback.fn (sw->callback.opaque, free);
}
}
}
if (hw->pcm_ops->run_buffer_out) {
hw->pcm_ops->run_buffer_out(hw);
}
@ -1211,13 +1259,6 @@ static void audio_run_out (AudioState *s)
if (!sw->total_hw_samples_mixed) {
sw->empty = 1;
}
if (sw->active) {
free = audio_get_free (sw);
if (free > 0) {
sw->callback.fn (sw->callback.opaque, free);
}
}
}
}
}
@ -1225,7 +1266,6 @@ static void audio_run_out (AudioState *s)
static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
{
size_t conv = 0;
STSampleBuffer *conv_buf = hw->conv_buf;
if (hw->pcm_ops->run_buffer_in) {
hw->pcm_ops->run_buffer_in(hw);
@ -1241,11 +1281,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
break;
}
proc = MIN(size / hw->info.bytes_per_frame,
conv_buf->size - conv_buf->pos);
hw->conv(conv_buf->samples + conv_buf->pos, buf, proc);
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);
samples -= proc;
conv += proc;
@ -1289,11 +1325,13 @@ static void audio_run_in (AudioState *s)
sw->total_hw_samples_acquired -= min;
if (sw->active) {
size_t sw_avail = audio_get_avail(sw);
size_t avail;
avail = audio_get_avail (sw);
avail = audio_frontend_frames_in(sw, sw_avail);
if (avail > 0) {
sw->callback.fn (sw->callback.opaque, avail);
sw->callback.fn(sw->callback.opaque,
avail * sw->info.bytes_per_frame);
}
}
}
@ -1394,12 +1432,10 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw)
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
{
ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
size_t start;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
assert(start < hw->size_emul);
*size = MIN(*size, hw->pending_emul);
*size = MIN(*size, hw->size_emul - start);
@ -1412,16 +1448,22 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
hw->pending_emul -= size;
}
size_t audio_generic_buffer_get_free(HWVoiceOut *hw)
{
if (hw->buf_emul) {
return hw->size_emul - hw->pending_emul;
} else {
return hw->samples * hw->info.bytes_per_frame;
}
}
void audio_generic_run_buffer_out(HWVoiceOut *hw)
{
while (hw->pending_emul) {
size_t write_len, written;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
size_t write_len, written, start;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
assert(start < hw->size_emul);
write_len = MIN(hw->pending_emul, hw->size_emul - start);
@ -1462,6 +1504,12 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
{
size_t total = 0;
if (hw->pcm_ops->buffer_get_free) {
size_t free = hw->pcm_ops->buffer_get_free(hw);
size = MIN(size, free);
}
while (total < size) {
size_t dst_size = size - total;
size_t copy_size, proc;
@ -1483,10 +1531,6 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
}
}
if (hw->pcm_ops->run_buffer_out) {
hw->pcm_ops->run_buffer_out(hw);
}
return total;
}
@ -1716,7 +1760,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
audio_validate_opts(dev, &error_abort);
}
s = g_malloc0(sizeof(AudioState));
s = g_new0(AudioState, 1);
s->dev = dev;
QLIST_INIT (&s->hw_head_out);
@ -1726,20 +1770,19 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
atexit(audio_cleanup);
atexit_registered = true;
}
QTAILQ_INSERT_TAIL(&audio_states, s, list);
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices;
s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices;
if (s->nb_hw_voices_out <= 0) {
if (s->nb_hw_voices_out < 1) {
dolog ("Bogus number of playback voices %d, setting to 1\n",
s->nb_hw_voices_out);
s->nb_hw_voices_out = 1;
}
if (s->nb_hw_voices_in <= 0) {
if (s->nb_hw_voices_in < 0) {
dolog ("Bogus number of capture voices %d, setting to 0\n",
s->nb_hw_voices_in);
s->nb_hw_voices_in = 0;
@ -1752,6 +1795,10 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
} else {
dolog ("Unknown audio driver `%s'\n", drvname);
}
if (!done) {
free_audio_state(s);
return NULL;
}
} else {
for (i = 0; audio_prio_list[i]; i++) {
AudiodevListEntry *e = audiodev_find(&head, audio_prio_list[i]);
@ -1789,6 +1836,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
"(Audio can continue looping even after stopping the VM)\n");
}
QTAILQ_INSERT_TAIL(&audio_states, s, list);
QLIST_INIT (&s->card_head);
vmstate_register (NULL, 0, &vmstate_audio, s);
return s;
@ -1821,6 +1869,7 @@ void AUD_remove_card (QEMUSoundCard *card)
g_free (card->name);
}
static struct audio_pcm_ops capture_pcm_ops;
CaptureVoiceOut *AUD_add_capture(
AudioState *s,
@ -1866,6 +1915,7 @@ CaptureVoiceOut *AUD_add_capture(
hw = &cap->hw;
hw->s = s;
hw->pcm_ops = &capture_pcm_ops;
QLIST_INIT (&hw->sw_head);
QLIST_INIT (&cap->cb_head);
@ -2000,11 +2050,13 @@ void audio_create_pdos(Audiodev *dev)
CASE(NONE, none, );
CASE(ALSA, alsa, Alsa);
CASE(COREAUDIO, coreaudio, Coreaudio);
CASE(DBUS, dbus, );
CASE(DSOUND, dsound, );
CASE(JACK, jack, Jack);
CASE(OSS, oss, Oss);
CASE(PA, pa, Pa);
CASE(SDL, sdl, Sdl);
CASE(SNDIO, sndio, );
CASE(SPICE, spice, );
CASE(WAV, wav, );
@ -2077,29 +2129,57 @@ static void audio_validate_opts(Audiodev *dev, Error **errp)
}
}
void audio_help(void)
{
int i;
printf("Available audio drivers:\n");
for (i = 0; i < AUDIODEV_DRIVER__MAX; i++) {
audio_driver *driver = audio_driver_lookup(AudiodevDriver_str(i));
if (driver) {
printf("%s\n", driver->name);
}
}
}
void audio_parse_option(const char *opt)
{
AudiodevListEntry *e;
Audiodev *dev = NULL;
if (is_help_option(opt)) {
audio_help();
exit(EXIT_SUCCESS);
}
Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal);
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v);
audio_define(dev);
}
void audio_define(Audiodev *dev)
{
AudiodevListEntry *e;
audio_validate_opts(dev, &error_fatal);
e = g_malloc0(sizeof(AudiodevListEntry));
e = g_new0(AudiodevListEntry, 1);
e->dev = dev;
QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
}
void audio_init_audiodevs(void)
bool audio_init_audiodevs(void)
{
AudiodevListEntry *e;
QSIMPLEQ_FOREACH(e, &audiodevs, next) {
audio_init(e->dev, NULL);
if (!audio_init(e->dev, NULL)) {
return false;
}
}
return true;
}
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
@ -2197,26 +2277,39 @@ void audio_rate_start(RateCtl *rate)
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t bytes_avail)
size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)
{
int64_t now;
int64_t ticks;
int64_t bytes;
int64_t samples;
size_t ret;
int64_t frames;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
samples = (bytes - rate->bytes_sent) / info->bytes_per_frame;
if (samples < 0 || samples > 65536) {
AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples);
frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;
if (frames < 0 || frames > 65536) {
AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames);
audio_rate_start(rate);
samples = 0;
frames = 0;
}
ret = MIN(samples * info->bytes_per_frame, bytes_avail);
rate->bytes_sent += ret;
return ret;
return frames * info->bytes_per_frame;
}
void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)
{
rate->bytes_sent += bytes_used;
}
size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
size_t bytes_avail)
{
size_t bytes;
bytes = audio_rate_peek_bytes(rate, info);
bytes = MIN(bytes, bytes_avail);
audio_rate_add_bytes(rate, bytes);
return bytes;
}

View File

@ -32,7 +32,7 @@
typedef void (*audio_callback_fn) (void *opaque, int avail);
#ifdef HOST_WORDS_BIGENDIAN
#if HOST_BIG_ENDIAN
#define AUDIO_HOST_ENDIANNESS 1
#else
#define AUDIO_HOST_ENDIANNESS 0
@ -91,8 +91,8 @@ typedef struct QEMUAudioTimeStamp {
uint64_t old_ts;
} QEMUAudioTimeStamp;
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
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);
void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card);
@ -168,8 +168,10 @@ void audio_sample_to_uint64(const void *samples, int pos,
void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right);
void audio_define(Audiodev *audio);
void audio_parse_option(const char *opt);
void audio_init_audiodevs(void);
bool audio_init_audiodevs(void);
void audio_help(void);
void audio_legacy_help(void);
AudioState *audio_state_by_name(const char *name);

View File

@ -31,6 +31,10 @@
#endif
#include "mixeng.h"
#ifdef CONFIG_GIO
#include <gio/gio.h>
#endif
struct audio_pcm_ops;
struct audio_callback {
@ -140,6 +144,9 @@ struct audio_driver {
const char *descr;
void *(*init) (Audiodev *);
void (*fini) (void *);
#ifdef CONFIG_GIO
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager);
#endif
struct audio_pcm_ops *pcm_ops;
int can_be_default;
int max_voices_out;
@ -154,10 +161,14 @@ struct audio_pcm_ops {
void (*fini_out)(HWVoiceOut *hw);
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
void (*run_buffer_out)(HWVoiceOut *hw);
/*
* Get the free output buffer size. This is an upper limit. The size
* returned by function get_buffer_out may be smaller.
*/
size_t (*buffer_get_free)(HWVoiceOut *hw);
/*
* get a buffer that after later can be passed to put_buffer_out; optional
* returns the buffer, and writes it's size to size (in bytes)
* this is unrelated to the above buffer_size_out function
*/
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
/*
@ -183,6 +194,7 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw);
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
void audio_generic_run_buffer_out(HWVoiceOut *hw);
size_t audio_generic_buffer_get_free(HWVoiceOut *hw);
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
@ -251,7 +263,9 @@ typedef struct RateCtl {
} RateCtl;
void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info);
void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used);
size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
size_t bytes_avail);
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
@ -259,6 +273,19 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
return (dst >= src) ? (dst - src) : (len - src + dst);
}
/**
* audio_ring_posb() - returns new position in ringbuffer in backward
* direction at given distance
*
* @pos: current position in ringbuffer
* @dist: distance in ringbuffer to walk in reverse direction
* @len: size of ringbuffer
*/
static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
{
return pos >= dist ? pos - dist : len - dist + pos;
}
#define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__)
#ifdef DEBUG

View File

@ -328,8 +328,8 @@ static void handle_per_direction(
static AudiodevListEntry *legacy_opt(const char *drvname)
{
AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
e->dev = g_malloc0(sizeof(Audiodev));
AudiodevListEntry *e = g_new0(AudiodevListEntry, 1);
e->dev = g_new0(Audiodev, 1);
e->dev->id = g_strdup(drvname);
e->dev->driver = qapi_enum_parse(
&AudiodevDriver_lookup, drvname, -1, &error_abort);
@ -508,7 +508,7 @@ static void lv_free(Visitor *v)
static Visitor *legacy_visitor_new(void)
{
LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
LegacyPrintVisitor *lv = g_new0(LegacyPrintVisitor, 1);
lv->visitor.start_struct = lv_start_struct;
lv->visitor.end_struct = lv_end_struct;

View File

@ -110,7 +110,11 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
return 0;
}
#ifdef DAC
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
#else
samples = (int64_t)sw->HWBUF->size * sw->ratio >> 32;
#endif
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) {
@ -327,6 +331,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
case AUDIODEV_DRIVER_COREAUDIO:
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
dev->u.coreaudio.TYPE);
case AUDIODEV_DRIVER_DBUS:
return dev->u.dbus.TYPE;
case AUDIODEV_DRIVER_DSOUND:
return dev->u.dsound.TYPE;
case AUDIODEV_DRIVER_JACK:
@ -337,6 +343,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
case AUDIODEV_DRIVER_SDL:
return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE);
case AUDIODEV_DRIVER_SNDIO:
return dev->u.sndio.TYPE;
case AUDIODEV_DRIVER_SPICE:
return dev->u.spice.TYPE;
case AUDIODEV_DRIVER_WAV:

View File

@ -1,7 +1,6 @@
/* public domain */
#include "qemu/osdep.h"
#include "qemu-common.h"
#define AUDIO_CAP "win-int"
#include <windows.h>

View File

@ -44,10 +44,15 @@ typedef struct coreaudioVoiceOut {
bool enabled;
} coreaudioVoiceOut;
#if !defined(MAC_OS_VERSION_12_0) \
|| (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0)
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
#endif
static const AudioObjectPropertyAddress voice_addr = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
kAudioObjectPropertyElementMain
};
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
@ -69,7 +74,7 @@ static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
AudioObjectPropertyAddress addr = {
kAudioDevicePropertyBufferFrameSizeRange,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
kAudioObjectPropertyElementMain
};
return AudioObjectGetPropertyData(id,
@ -86,7 +91,7 @@ static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
AudioObjectPropertyAddress addr = {
kAudioDevicePropertyBufferFrameSize,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
kAudioObjectPropertyElementMain
};
return AudioObjectGetPropertyData(id,
@ -103,7 +108,7 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
AudioObjectPropertyAddress addr = {
kAudioDevicePropertyBufferFrameSize,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
kAudioObjectPropertyElementMain
};
return AudioObjectSetPropertyData(id,
@ -121,7 +126,7 @@ static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
AudioObjectPropertyAddress addr = {
kAudioDevicePropertyStreamFormat,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
kAudioObjectPropertyElementMain
};
return AudioObjectSetPropertyData(id,
@ -138,7 +143,7 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
AudioObjectPropertyAddress addr = {
kAudioDevicePropertyDeviceIsRunning,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
kAudioObjectPropertyElementMain
};
return AudioObjectGetPropertyData(id,
@ -206,7 +211,7 @@ static void coreaudio_logstatus (OSStatus status)
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
}
static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
OSStatus status,
const char *fmt,
...
@ -221,7 +226,7 @@ static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
coreaudio_logstatus (status);
}
static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
OSStatus status,
const char *typ,
const char *fmt,
@ -283,6 +288,7 @@ static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
coreaudio_buf_unlock(core, "coreaudio_" #name); \
return ret; \
}
COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size))
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
@ -333,12 +339,10 @@ static OSStatus audioDeviceIOProc(
len = frameCount * hw->info.bytes_per_frame;
while (len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
size_t write_len, start;
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
assert(start < hw->size_emul);
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
@ -541,7 +545,6 @@ static OSStatus handle_voice_change(
const AudioObjectPropertyAddress *in_addresses,
void *in_client_data)
{
OSStatus status;
coreaudioVoiceOut *core = in_client_data;
qemu_mutex_lock_iothread();
@ -550,13 +553,12 @@ static OSStatus handle_voice_change(
fini_out_device(core);
}
status = init_out_device(core);
if (!status) {
if (!init_out_device(core)) {
update_device_playback_state(core);
}
qemu_mutex_unlock_iothread();
return status;
return 0;
}
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
@ -604,6 +606,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
coreaudio_playback_logerr(status,
"Could not remove voice property change listener\n");
}
return -1;
}
return 0;
@ -654,6 +658,8 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
.fini_out = coreaudio_fini_out,
/* wrapper for audio_generic_write */
.write = coreaudio_write,
/* wrapper for audio_generic_buffer_get_free */
.buffer_get_free = coreaudio_buffer_get_free,
/* wrapper for audio_generic_get_buffer_out */
.get_buffer_out = coreaudio_get_buffer_out,
/* wrapper for audio_generic_put_buffer_out */

654
audio/dbusaudio.c Normal file
View File

@ -0,0 +1,654 @@
/*
* QEMU DBus audio
*
* Copyright (c) 2021 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "qemu/dbus.h"
#include <gio/gunixfdlist.h>
#include "ui/dbus-display1.h"
#define AUDIO_CAP "dbus"
#include "audio.h"
#include "audio_int.h"
#include "trace.h"
#define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
#define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
typedef struct DBusAudio {
GDBusObjectManagerServer *server;
GDBusObjectSkeleton *audio;
QemuDBusDisplay1Audio *iface;
GHashTable *out_listeners;
GHashTable *in_listeners;
} DBusAudio;
typedef struct DBusVoiceOut {
HWVoiceOut hw;
bool enabled;
RateCtl rate;
void *buf;
size_t buf_pos;
size_t buf_size;
bool has_volume;
Volume volume;
} DBusVoiceOut;
typedef struct DBusVoiceIn {
HWVoiceIn hw;
bool enabled;
RateCtl rate;
bool has_volume;
Volume volume;
} DBusVoiceIn;
static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
if (!vo->buf) {
vo->buf_size = hw->samples * hw->info.bytes_per_frame;
vo->buf = g_malloc(vo->buf_size);
vo->buf_pos = 0;
}
*size = MIN(vo->buf_size - vo->buf_pos, *size);
*size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
return vo->buf + vo->buf_pos;
}
static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioOutListener *listener = NULL;
g_autoptr(GBytes) bytes = NULL;
g_autoptr(GVariant) v_data = NULL;
assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
vo->buf_pos += size;
trace_dbus_audio_put_buffer_out(size);
if (vo->buf_pos < vo->buf_size) {
return size;
}
bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
g_variant_ref_sink(v_data);
g_hash_table_iter_init(&iter, da->out_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
qemu_dbus_display1_audio_out_listener_call_write(
listener,
(uintptr_t)hw,
v_data,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
return size;
}
#if HOST_BIG_ENDIAN
#define AUDIO_HOST_BE TRUE
#else
#define AUDIO_HOST_BE FALSE
#endif
static void
dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
HWVoiceOut *hw)
{
qemu_dbus_display1_audio_out_listener_call_init(
listener,
(uintptr_t)hw,
hw->info.bits,
hw->info.is_signed,
hw->info.is_float,
hw->info.freq,
hw->info.nchannels,
hw->info.bytes_per_frame,
hw->info.bytes_per_second,
hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static int
dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioOutListener *listener = NULL;
audio_pcm_init_info(&hw->info, as);
hw->samples = DBUS_AUDIO_NSAMPLES;
audio_rate_start(&vo->rate);
g_hash_table_iter_init(&iter, da->out_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
dbus_init_out_listener(listener, hw);
}
return 0;
}
static void
dbus_fini_out(HWVoiceOut *hw)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioOutListener *listener = NULL;
g_hash_table_iter_init(&iter, da->out_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
qemu_dbus_display1_audio_out_listener_call_fini(
listener,
(uintptr_t)hw,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
g_clear_pointer(&vo->buf, g_free);
}
static void
dbus_enable_out(HWVoiceOut *hw, bool enable)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioOutListener *listener = NULL;
vo->enabled = enable;
if (enable) {
audio_rate_start(&vo->rate);
}
g_hash_table_iter_init(&iter, da->out_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
qemu_dbus_display1_audio_out_listener_call_set_enabled(
listener, (uintptr_t)hw, enable,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
}
static void
dbus_volume_out_listener(HWVoiceOut *hw,
QemuDBusDisplay1AudioOutListener *listener)
{
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
Volume *vol = &vo->volume;
g_autoptr(GBytes) bytes = NULL;
GVariant *v_vol = NULL;
if (!vo->has_volume) {
return;
}
assert(vol->channels < sizeof(vol->vol));
bytes = g_bytes_new(vol->vol, vol->channels);
v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
qemu_dbus_display1_audio_out_listener_call_set_volume(
listener, (uintptr_t)hw, vol->mute, v_vol,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
dbus_volume_out(HWVoiceOut *hw, Volume *vol)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioOutListener *listener = NULL;
vo->has_volume = true;
vo->volume = *vol;
g_hash_table_iter_init(&iter, da->out_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
dbus_volume_out_listener(hw, listener);
}
}
static void
dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
{
qemu_dbus_display1_audio_in_listener_call_init(
listener,
(uintptr_t)hw,
hw->info.bits,
hw->info.is_signed,
hw->info.is_float,
hw->info.freq,
hw->info.nchannels,
hw->info.bytes_per_frame,
hw->info.bytes_per_second,
hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static int
dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioInListener *listener = NULL;
audio_pcm_init_info(&hw->info, as);
hw->samples = DBUS_AUDIO_NSAMPLES;
audio_rate_start(&vo->rate);
g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
dbus_init_in_listener(listener, hw);
}
return 0;
}
static void
dbus_fini_in(HWVoiceIn *hw)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
GHashTableIter iter;
QemuDBusDisplay1AudioInListener *listener = NULL;
g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
qemu_dbus_display1_audio_in_listener_call_fini(
listener,
(uintptr_t)hw,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
}
static void
dbus_volume_in_listener(HWVoiceIn *hw,
QemuDBusDisplay1AudioInListener *listener)
{
DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
Volume *vol = &vo->volume;
g_autoptr(GBytes) bytes = NULL;
GVariant *v_vol = NULL;
if (!vo->has_volume) {
return;
}
assert(vol->channels < sizeof(vol->vol));
bytes = g_bytes_new(vol->vol, vol->channels);
v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
qemu_dbus_display1_audio_in_listener_call_set_volume(
listener, (uintptr_t)hw, vol->mute, v_vol,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
dbus_volume_in(HWVoiceIn *hw, Volume *vol)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioInListener *listener = NULL;
vo->has_volume = true;
vo->volume = *vol;
g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
dbus_volume_in_listener(hw, listener);
}
}
static size_t
dbus_read(HWVoiceIn *hw, void *buf, size_t size)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
/* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
GHashTableIter iter;
QemuDBusDisplay1AudioInListener *listener = NULL;
trace_dbus_audio_read(size);
/* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
g_autoptr(GVariant) v_data = NULL;
const char *data;
gsize n = 0;
if (qemu_dbus_display1_audio_in_listener_call_read_sync(
listener,
(uintptr_t)hw,
size,
G_DBUS_CALL_FLAGS_NONE, -1,
&v_data, NULL, NULL)) {
data = g_variant_get_fixed_array(v_data, &n, 1);
g_warn_if_fail(n <= size);
size = MIN(n, size);
memcpy(buf, data, size);
break;
}
}
return size;
}
static void
dbus_enable_in(HWVoiceIn *hw, bool enable)
{
DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
GHashTableIter iter;
QemuDBusDisplay1AudioInListener *listener = NULL;
vo->enabled = enable;
if (enable) {
audio_rate_start(&vo->rate);
}
g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
qemu_dbus_display1_audio_in_listener_call_set_enabled(
listener, (uintptr_t)hw, enable,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
}
static void *
dbus_audio_init(Audiodev *dev)
{
DBusAudio *da = g_new0(DBusAudio, 1);
da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_object_unref);
da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_object_unref);
return da;
}
static void
dbus_audio_fini(void *opaque)
{
DBusAudio *da = opaque;
if (da->server) {
g_dbus_object_manager_server_unexport(da->server,
DBUS_DISPLAY1_AUDIO_PATH);
}
g_clear_object(&da->audio);
g_clear_object(&da->iface);
g_clear_pointer(&da->in_listeners, g_hash_table_unref);
g_clear_pointer(&da->out_listeners, g_hash_table_unref);
g_clear_object(&da->server);
g_free(da);
}
static void
listener_out_vanished_cb(GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error,
DBusAudio *da)
{
char *name = g_object_get_data(G_OBJECT(connection), "name");
g_hash_table_remove(da->out_listeners, name);
}
static void
listener_in_vanished_cb(GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error,
DBusAudio *da)
{
char *name = g_object_get_data(G_OBJECT(connection), "name");
g_hash_table_remove(da->in_listeners, name);
}
static gboolean
dbus_audio_register_listener(AudioState *s,
GDBusMethodInvocation *invocation,
GUnixFDList *fd_list,
GVariant *arg_listener,
bool out)
{
DBusAudio *da = s->drv_opaque;
const char *sender = g_dbus_method_invocation_get_sender(invocation);
g_autoptr(GDBusConnection) listener_conn = NULL;
g_autoptr(GError) err = NULL;
g_autoptr(GSocket) socket = NULL;
g_autoptr(GSocketConnection) socket_conn = NULL;
g_autofree char *guid = g_dbus_generate_guid();
GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
GObject *listener;
int fd;
trace_dbus_audio_register(sender, out ? "out" : "in");
if (g_hash_table_contains(listeners, sender)) {
g_dbus_method_invocation_return_error(invocation,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_INVALID,
"`%s` is already registered!",
sender);
return DBUS_METHOD_INVOCATION_HANDLED;
}
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
if (err) {
g_dbus_method_invocation_return_error(invocation,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't get peer fd: %s",
err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
socket = g_socket_new_from_fd(fd, &err);
if (err) {
g_dbus_method_invocation_return_error(invocation,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't make a socket: %s",
err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
socket_conn = g_socket_connection_factory_create_connection(socket);
if (out) {
qemu_dbus_display1_audio_complete_register_out_listener(
da->iface, invocation, NULL);
} else {
qemu_dbus_display1_audio_complete_register_in_listener(
da->iface, invocation, NULL);
}
listener_conn =
g_dbus_connection_new_sync(
G_IO_STREAM(socket_conn),
guid,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
NULL, NULL, &err);
if (err) {
error_report("Failed to setup peer connection: %s", err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
listener = out ?
G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
listener_conn,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
"/org/qemu/Display1/AudioOutListener",
NULL,
&err)) :
G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
listener_conn,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
"/org/qemu/Display1/AudioInListener",
NULL,
&err));
if (!listener) {
error_report("Failed to setup proxy: %s", err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
if (out) {
HWVoiceOut *hw;
QLIST_FOREACH(hw, &s->hw_head_out, entries) {
DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
QemuDBusDisplay1AudioOutListener *l =
QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
dbus_init_out_listener(l, hw);
qemu_dbus_display1_audio_out_listener_call_set_enabled(
l, (uintptr_t)hw, vo->enabled,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
} else {
HWVoiceIn *hw;
QLIST_FOREACH(hw, &s->hw_head_in, entries) {
DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
QemuDBusDisplay1AudioInListener *l =
QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
dbus_init_in_listener(
QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
qemu_dbus_display1_audio_in_listener_call_set_enabled(
l, (uintptr_t)hw, vo->enabled,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
}
g_object_set_data_full(G_OBJECT(listener_conn), "name",
g_strdup(sender), g_free);
g_hash_table_insert(listeners, g_strdup(sender), listener);
g_object_connect(listener_conn,
"signal::closed",
out ? listener_out_vanished_cb : listener_in_vanished_cb,
da,
NULL);
return DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
dbus_audio_register_out_listener(AudioState *s,
GDBusMethodInvocation *invocation,
GUnixFDList *fd_list,
GVariant *arg_listener)
{
return dbus_audio_register_listener(s, invocation,
fd_list, arg_listener, true);
}
static gboolean
dbus_audio_register_in_listener(AudioState *s,
GDBusMethodInvocation *invocation,
GUnixFDList *fd_list,
GVariant *arg_listener)
{
return dbus_audio_register_listener(s, invocation,
fd_list, arg_listener, false);
}
static void
dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server)
{
DBusAudio *da = s->drv_opaque;
g_assert(da);
g_assert(!da->server);
da->server = g_object_ref(server);
da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
da->iface = qemu_dbus_display1_audio_skeleton_new();
g_object_connect(da->iface,
"swapped-signal::handle-register-in-listener",
dbus_audio_register_in_listener, s,
"swapped-signal::handle-register-out-listener",
dbus_audio_register_out_listener, s,
NULL);
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
G_DBUS_INTERFACE_SKELETON(da->iface));
g_dbus_object_manager_server_export(da->server, da->audio);
}
static struct audio_pcm_ops dbus_pcm_ops = {
.init_out = dbus_init_out,
.fini_out = dbus_fini_out,
.write = audio_generic_write,
.get_buffer_out = dbus_get_buffer_out,
.put_buffer_out = dbus_put_buffer_out,
.enable_out = dbus_enable_out,
.volume_out = dbus_volume_out,
.init_in = dbus_init_in,
.fini_in = dbus_fini_in,
.read = dbus_read,
.run_buffer_in = audio_generic_run_buffer_in,
.enable_in = dbus_enable_in,
.volume_in = dbus_volume_in,
};
static struct audio_driver dbus_audio_driver = {
.name = "dbus",
.descr = "Timer based audio exposed with DBus interface",
.init = dbus_audio_init,
.fini = dbus_audio_fini,
.set_dbus_server = dbus_audio_set_server,
.pcm_ops = &dbus_pcm_ops,
.can_be_default = 1,
.max_voices_out = INT_MAX,
.max_voices_in = INT_MAX,
.voice_size_out = sizeof(DBusVoiceOut),
.voice_size_in = sizeof(DBusVoiceIn)
};
static void register_audio_dbus(void)
{
audio_driver_register(&dbus_audio_driver);
}
type_init(register_audio_dbus);
module_dep("ui-dbus")

View File

@ -222,7 +222,7 @@ static void dsound_log_hresult (HRESULT hr)
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
}
static void GCC_FMT_ATTR (2, 3) dsound_logerr (
static void G_GNUC_PRINTF (2, 3) dsound_logerr (
HRESULT hr,
const char *fmt,
...
@ -237,7 +237,7 @@ static void GCC_FMT_ATTR (2, 3) dsound_logerr (
dsound_log_hresult (hr);
}
static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
static void G_GNUC_PRINTF (3, 4) dsound_logerr2 (
HRESULT hr,
const char *typ,
const char *fmt,
@ -427,22 +427,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
}
}
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
static size_t dsound_buffer_get_free(HWVoiceOut *hw)
{
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
HRESULT hr;
DWORD ppos, wpos, act_size;
size_t req_size;
int err;
void *ret;
DWORD ppos, wpos;
hr = IDirectSoundBuffer_GetCurrentPosition(
dsb, &ppos, ds->first_time ? &wpos : NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get playback buffer position\n");
*size = 0;
return NULL;
return 0;
}
if (ds->first_time) {
@ -450,13 +446,20 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
ds->first_time = false;
}
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
}
if (req_size == 0) {
*size = 0;
return NULL;
}
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
DSoundVoiceOut *ds = (DSoundVoiceOut *)hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
DWORD act_size;
size_t req_size;
int err;
void *ret;
req_size = MIN(*size, hw->size_emul - hw->pos_emul);
assert(req_size > 0);
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
&act_size, NULL, false, ds->s);
@ -536,13 +539,12 @@ static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
HRESULT hr;
DWORD cpos, rpos, act_size;
DWORD rpos, act_size;
size_t req_size;
int err;
void *ret;
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
dscb, &cpos, ds->first_time ? &rpos : NULL);
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, NULL, &rpos);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get capture buffer position\n");
*size = 0;
@ -554,7 +556,7 @@ static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
ds->first_time = false;
}
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
req_size = audio_ring_dist(rpos, hw->pos_emul, hw->size_emul);
req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
if (req_size == 0) {
@ -621,7 +623,7 @@ static void *dsound_audio_init(Audiodev *dev)
{
int err;
HRESULT hr;
dsound *s = g_malloc0(sizeof(dsound));
dsound *s = g_new0(dsound, 1);
AudiodevDsoundOptions *dso;
assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
@ -700,6 +702,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out,
.fini_out = dsound_fini_out,
.write = audio_generic_write,
.buffer_get_free = dsound_buffer_get_free,
.get_buffer_out = dsound_get_buffer_out,
.put_buffer_out = dsound_put_buffer_out,
.enable_out = dsound_enable_out,

View File

@ -97,9 +97,9 @@ static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames)
buffer->used = 0;
buffer->rptr = 0;
buffer->wptr = 0;
buffer->data = g_malloc(channels * sizeof(float *));
buffer->data = g_new(float *, channels);
for (int i = 0; i < channels; ++i) {
buffer->data[i] = g_malloc(frames * sizeof(float));
buffer->data[i] = g_new(float, frames);
}
}
@ -453,7 +453,7 @@ static int qjack_client_init(QJackClient *c)
jack_on_shutdown(c->client, qjack_shutdown, c);
/* allocate and register the ports */
c->port = g_malloc(sizeof(jack_port_t *) * c->nchannels);
c->port = g_new(jack_port_t *, c->nchannels);
for (int i = 0; i < c->nchannels; ++i) {
char port_name[16];
@ -483,8 +483,8 @@ static int qjack_client_init(QJackClient *c)
c->buffersize = 512;
}
/* create a 2 period buffer */
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2);
/* create a 3 period buffer */
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 3);
qjack_client_connect_ports(c);
c->state = QJACK_STATE_RUNNING;
@ -622,6 +622,7 @@ static void qjack_enable_in(HWVoiceIn *hw, bool enable)
ji->c.enabled = enable;
}
#if !defined(WIN32) && defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
static int qjack_thread_creator(jack_native_thread_t *thread,
const pthread_attr_t *attr, void *(*function)(void *), void *arg)
{
@ -635,6 +636,7 @@ static int qjack_thread_creator(jack_native_thread_t *thread,
return ret;
}
#endif
static void *qjack_init(Audiodev *dev)
{
@ -650,6 +652,7 @@ static struct audio_pcm_ops jack_pcm_ops = {
.init_out = qjack_init_out,
.fini_out = qjack_fini_out,
.write = qjack_write,
.buffer_get_free = audio_generic_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = qjack_enable_out,
@ -687,7 +690,9 @@ static void register_audio_jack(void)
{
qemu_mutex_init(&qjack_shutdown_lock);
audio_driver_register(&jack_driver);
#if !defined(WIN32) && defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
jack_set_thread_creator(qjack_thread_creator);
#endif
jack_set_error_function(qjack_error);
jack_set_info_function(qjack_info);
}

View File

@ -7,24 +7,30 @@ softmmu_ss.add(files(
'wavcapture.c',
))
softmmu_ss.add(when: [coreaudio, 'CONFIG_AUDIO_COREAUDIO'], if_true: files('coreaudio.c'))
softmmu_ss.add(when: [dsound, 'CONFIG_AUDIO_DSOUND'], if_true: files('dsoundaudio.c'))
softmmu_ss.add(when: ['CONFIG_AUDIO_WIN_INT'], if_true: files('audio_win_int.c'))
softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.m'))
softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))
audio_modules = {}
foreach m : [
['CONFIG_AUDIO_ALSA', 'alsa', alsa, 'alsaaudio.c'],
['CONFIG_AUDIO_OSS', 'oss', oss, 'ossaudio.c'],
['CONFIG_AUDIO_PA', 'pa', pulse, 'paaudio.c'],
['CONFIG_AUDIO_SDL', 'sdl', sdl, 'sdlaudio.c'],
['CONFIG_AUDIO_JACK', 'jack', jack, 'jackaudio.c'],
['CONFIG_SPICE', 'spice', spice, 'spiceaudio.c']
['alsa', alsa, files('alsaaudio.c')],
['oss', oss, files('ossaudio.c')],
['pa', pulse, files('paaudio.c')],
['sdl', sdl, files('sdlaudio.c')],
['jack', jack, files('jackaudio.c')],
['sndio', sndio, files('sndioaudio.c')],
['spice', spice, files('spiceaudio.c')]
]
if config_host.has_key(m[0])
if m[1].found()
module_ss = ss.source_set()
module_ss.add(when: m[2], if_true: files(m[3]))
audio_modules += {m[1] : module_ss}
module_ss.add(m[1], m[2])
audio_modules += {m[0] : module_ss}
endif
endforeach
if dbus_display
module_ss = ss.source_set()
module_ss.add(when: gio, if_true: files('dbusaudio.c'))
audio_modules += {'dbus': module_ss}
endif
modules += {'audio': audio_modules}

View File

@ -342,13 +342,13 @@ f_sample *mixeng_clip_float[2] = {
void audio_sample_to_uint64(const void *samples, int pos,
uint64_t *left, uint64_t *right)
{
const struct st_sample *sample = samples;
sample += pos;
#ifdef FLOAT_MIXENG
error_report(
"Coreaudio and floating point samples are not supported by replay yet");
abort();
#else
const struct st_sample *sample = samples;
sample += pos;
*left = sample->l;
*right = sample->r;
#endif
@ -357,13 +357,13 @@ void audio_sample_to_uint64(const void *samples, int pos,
void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right)
{
struct st_sample *sample = samples;
sample += pos;
#ifdef FLOAT_MIXENG
error_report(
"Coreaudio and floating point samples are not supported by replay yet");
abort();
#else
struct st_sample *sample = samples;
sample += pos;
sample->l = left;
sample->r = right;
#endif

View File

@ -44,7 +44,7 @@ typedef struct NoVoiceIn {
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
return audio_rate_get_bytes(&hw->info, &no->rate, len);
return audio_rate_get_bytes(&no->rate, &hw->info, len);
}
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
@ -89,7 +89,7 @@ static void no_fini_in (HWVoiceIn *hw)
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
int64_t bytes = audio_rate_get_bytes(&no->rate, &hw->info, size);
audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame);
return bytes;
@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = {
.init_out = no_init_out,
.fini_out = no_fini_out,
.write = no_write,
.buffer_get_free = audio_generic_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = no_enable_out,

View File

@ -63,7 +63,7 @@ struct oss_params {
int fragsize;
};
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
static void G_GNUC_PRINTF (2, 3) oss_logerr (int err, const char *fmt, ...)
{
va_list ap;
@ -74,7 +74,7 @@ static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
}
static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
static void G_GNUC_PRINTF (3, 4) oss_logerr2 (
int err,
const char *typ,
const char *fmt,
@ -389,11 +389,23 @@ static void oss_run_buffer_out(HWVoiceOut *hw)
}
}
static size_t oss_buffer_get_free(HWVoiceOut *hw)
{
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
if (oss->mmapped) {
return oss_get_available_bytes(oss);
} else {
return audio_generic_buffer_get_free(hw);
}
}
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
if (oss->mmapped) {
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
*size = hw->size_emul - hw->pos_emul;
return hw->buf_emul + hw->pos_emul;
} else {
return audio_generic_get_buffer_out(hw, size);
@ -750,6 +762,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out,
.fini_out = oss_fini_out,
.write = oss_write,
.buffer_get_free = oss_buffer_get_free,
.run_buffer_out = oss_run_buffer_out,
.get_buffer_out = oss_get_buffer_out,
.put_buffer_out = oss_put_buffer_out,

View File

@ -43,7 +43,7 @@ typedef struct {
static void qpa_conn_fini(PAConnection *c);
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
static void G_GNUC_PRINTF (2, 3) qpa_logerr (int err, const char *fmt, ...)
{
va_list ap;
@ -201,13 +201,11 @@ unlock_and_fail:
return 0;
}
static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
static size_t qpa_buffer_get_free(HWVoiceOut *hw)
{
PAVoiceOut *p = (PAVoiceOut *) hw;
PAVoiceOut *p = (PAVoiceOut *)hw;
PAConnection *c = p->g->conn;
void *ret;
size_t l;
int r;
pa_threaded_mainloop_lock(c->mainloop);
@ -216,7 +214,6 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
/* wait for stream to become ready */
l = 0;
ret = NULL;
goto unlock;
}
@ -224,16 +221,33 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
"pa_stream_writable_size failed\n");
unlock:
pa_threaded_mainloop_unlock(c->mainloop);
return l;
unlock_and_fail:
pa_threaded_mainloop_unlock(c->mainloop);
return 0;
}
static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
PAVoiceOut *p = (PAVoiceOut *)hw;
PAConnection *c = p->g->conn;
void *ret;
int r;
pa_threaded_mainloop_lock(c->mainloop);
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
"pa_threaded_mainloop_lock failed\n");
*size = -1;
r = pa_stream_begin_write(p->stream, &ret, size);
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
"pa_stream_begin_write failed\n");
unlock:
pa_threaded_mainloop_unlock(c->mainloop);
if (*size > l) {
*size = l;
}
return ret;
unlock_and_fail:
@ -535,11 +549,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
audio_pcm_init_info (&hw->info, &obt_as);
/*
* This is wrong. hw->samples counts in frames. hw->samples will be
* number of channels times larger than expected.
*/
hw->samples = audio_buffer_samples(
/* hw->samples counts in frames */
hw->samples = audio_buffer_frames(
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
return 0;
@ -587,11 +598,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
audio_pcm_init_info (&hw->info, &obt_as);
/*
* This is wrong. hw->samples counts in frames. hw->samples will be
* number of channels times larger than expected.
*/
hw->samples = audio_buffer_samples(
/* hw->samples counts in frames */
hw->samples = audio_buffer_frames(
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
return 0;
@ -744,7 +752,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
{
if (!pdo->has_latency) {
pdo->has_latency = true;
pdo->latency = 15000;
pdo->latency = 46440;
}
return 1;
}
@ -752,7 +760,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
/* common */
static void *qpa_conn_init(const char *server)
{
PAConnection *c = g_malloc0(sizeof(PAConnection));
PAConnection *c = g_new0(PAConnection, 1);
QTAILQ_INSERT_TAIL(&pa_conns, c, list);
c->mainloop = pa_threaded_mainloop_new();
@ -841,7 +849,7 @@ static void *qpa_audio_init(Audiodev *dev)
return NULL;
}
g = g_malloc0(sizeof(paaudio));
g = g_new0(paaudio, 1);
server = popts->has_server ? popts->server : NULL;
g->dev = dev;
@ -901,6 +909,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
.init_out = qpa_init_out,
.fini_out = qpa_fini_out,
.write = qpa_write,
.buffer_get_free = qpa_buffer_get_free,
.get_buffer_out = qpa_get_buffer_out,
.put_buffer_out = qpa_put_buffer_out,
.volume_out = qpa_volume_out,

View File

@ -72,11 +72,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
ilast = *ibuf++;
rate->ipos++;
/* if ipos overflow, there is a infinite loop */
if (rate->ipos == 0xffffffff) {
rate->ipos = 1;
rate->opos = rate->opos & 0xffffffff;
}
/* See if we finished the input buffer yet */
if (ibuf >= iend) {
goto the_end;
@ -85,6 +80,12 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
icur = *ibuf;
/* wrap ipos and opos around long before they overflow */
if (rate->ipos >= 0x10001) {
rate->ipos = 1;
rate->opos &= 0xffffffff;
}
/* interpolate */
#ifdef FLOAT_MIXENG
#ifdef RECIPROCAL

View File

@ -55,7 +55,7 @@ typedef struct SDLVoiceIn {
SDL_AudioDeviceID devid;
} SDLVoiceIn;
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
static void G_GNUC_PRINTF (1, 2) sdl_logerr (const char *fmt, ...)
{
va_list ap;
@ -224,12 +224,11 @@ static void sdl_callback_out(void *opaque, Uint8 *buf, int len)
/* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */
while (hw->pending_emul && len) {
size_t write_len;
ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
size_t write_len, start;
start = audio_ring_posb(hw->pos_emul, hw->pending_emul,
hw->size_emul);
assert(start < hw->size_emul);
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
@ -310,6 +309,7 @@ static void sdl_callback_in(void *opaque, Uint8 *buf, int len)
SDL_UnlockAudioDevice(sdl->devid); \
}
SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out)
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size), Out)
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
@ -347,11 +347,8 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
req.freq = as->freq;
req.format = aud_to_sdlfmt (as->fmt);
req.channels = as->nchannels;
/*
* This is wrong. SDL samples are QEMU frames. The buffer size will be
* the requested buffer size multiplied by the number of channels.
*/
req.samples = audio_buffer_samples(
/* SDL samples are QEMU frames */
req.samples = audio_buffer_frames(
qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
req.callback = sdl_callback_out;
req.userdata = sdl;
@ -472,6 +469,8 @@ static struct audio_pcm_ops sdl_pcm_ops = {
.fini_out = sdl_fini_out,
/* wrapper for audio_generic_write */
.write = sdl_write,
/* wrapper for audio_generic_buffer_get_free */
.buffer_get_free = sdl_buffer_get_free,
/* wrapper for audio_generic_get_buffer_out */
.get_buffer_out = sdl_get_buffer_out,
/* wrapper for audio_generic_put_buffer_out */

565
audio/sndioaudio.c Normal file
View File

@ -0,0 +1,565 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019 Alexandre Ratchov <alex@caoua.org>
*/
/*
* TODO :
*
* Use a single device and open it in full-duplex rather than
* opening it twice (once for playback once for recording).
*
* This is the only way to ensure that playback doesn't drift with respect
* to recording, which is what guest systems expect.
*/
#include <poll.h>
#include <sndio.h>
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "audio.h"
#include "trace.h"
#define AUDIO_CAP "sndio"
#include "audio_int.h"
/* default latency in microseconds if no option is set */
#define SNDIO_LATENCY_US 50000
typedef struct SndioVoice {
union {
HWVoiceOut out;
HWVoiceIn in;
} hw;
struct sio_par par;
struct sio_hdl *hdl;
struct pollfd *pfds;
struct pollindex {
struct SndioVoice *self;
int index;
} *pindexes;
unsigned char *buf;
size_t buf_size;
size_t sndio_pos;
size_t qemu_pos;
unsigned int mode;
unsigned int nfds;
bool enabled;
} SndioVoice;
typedef struct SndioConf {
const char *devname;
unsigned int latency;
} SndioConf;
/* needed for forward reference */
static void sndio_poll_in(void *arg);
static void sndio_poll_out(void *arg);
/*
* stop polling descriptors
*/
static void sndio_poll_clear(SndioVoice *self)
{
struct pollfd *pfd;
int i;
for (i = 0; i < self->nfds; i++) {
pfd = &self->pfds[i];
qemu_set_fd_handler(pfd->fd, NULL, NULL, NULL);
}
self->nfds = 0;
}
/*
* write data to the device until it blocks or
* all of our buffered data is written
*/
static void sndio_write(SndioVoice *self)
{
size_t todo, n;
todo = self->qemu_pos - self->sndio_pos;
/*
* transfer data to device, until it blocks
*/
while (todo > 0) {
n = sio_write(self->hdl, self->buf + self->sndio_pos, todo);
if (n == 0) {
break;
}
self->sndio_pos += n;
todo -= n;
}
if (self->sndio_pos == self->buf_size) {
/*
* we complete the block
*/
self->sndio_pos = 0;
self->qemu_pos = 0;
}
}
/*
* read data from the device until it blocks or
* there no room any longer
*/
static void sndio_read(SndioVoice *self)
{
size_t todo, n;
todo = self->buf_size - self->sndio_pos;
/*
* transfer data from the device, until it blocks
*/
while (todo > 0) {
n = sio_read(self->hdl, self->buf + self->sndio_pos, todo);
if (n == 0) {
break;
}
self->sndio_pos += n;
todo -= n;
}
}
/*
* Set handlers for all descriptors libsndio needs to
* poll
*/
static void sndio_poll_wait(SndioVoice *self)
{
struct pollfd *pfd;
int events, i;
events = 0;
if (self->mode == SIO_PLAY) {
if (self->sndio_pos < self->qemu_pos) {
events |= POLLOUT;
}
} else {
if (self->sndio_pos < self->buf_size) {
events |= POLLIN;
}
}
/*
* fill the given array of descriptors with the events sndio
* wants, they are different from our 'event' variable because
* sndio may use descriptors internally.
*/
self->nfds = sio_pollfd(self->hdl, self->pfds, events);
for (i = 0; i < self->nfds; i++) {
pfd = &self->pfds[i];
if (pfd->fd < 0) {
continue;
}
qemu_set_fd_handler(pfd->fd,
(pfd->events & POLLIN) ? sndio_poll_in : NULL,
(pfd->events & POLLOUT) ? sndio_poll_out : NULL,
&self->pindexes[i]);
pfd->revents = 0;
}
}
/*
* call-back called when one of the descriptors
* became readable or writable
*/
static void sndio_poll_event(SndioVoice *self, int index, int event)
{
int revents;
/*
* ensure we're not called twice this cycle
*/
sndio_poll_clear(self);
/*
* make self->pfds[] look as we're returning from poll syscal,
* this is how sio_revents expects events to be.
*/
self->pfds[index].revents = event;
/*
* tell sndio to handle events and return whether we can read or
* write without blocking.
*/
revents = sio_revents(self->hdl, self->pfds);
if (self->mode == SIO_PLAY) {
if (revents & POLLOUT) {
sndio_write(self);
}
if (self->qemu_pos < self->buf_size) {
audio_run(self->hw.out.s, "sndio_out");
}
} else {
if (revents & POLLIN) {
sndio_read(self);
}
if (self->qemu_pos < self->sndio_pos) {
audio_run(self->hw.in.s, "sndio_in");
}
}
/*
* audio_run() may have changed state
*/
if (self->enabled) {
sndio_poll_wait(self);
}
}
/*
* return the upper limit of the amount of free play buffer space
*/
static size_t sndio_buffer_get_free(HWVoiceOut *hw)
{
SndioVoice *self = (SndioVoice *) hw;
return self->buf_size - self->qemu_pos;
}
/*
* return a buffer where data to play can be stored,
* its size is stored in the location pointed by the size argument.
*/
static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
SndioVoice *self = (SndioVoice *) hw;
*size = self->buf_size - self->qemu_pos;
return self->buf + self->qemu_pos;
}
/*
* put back to sndio back-end a buffer returned by sndio_get_buffer_out()
*/
static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
{
SndioVoice *self = (SndioVoice *) hw;
self->qemu_pos += size;
sndio_poll_wait(self);
return size;
}
/*
* return a buffer from where recorded data is available,
* its size is stored in the location pointed by the size argument.
* it may not exceed the initial value of "*size".
*/
static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size)
{
SndioVoice *self = (SndioVoice *) hw;
size_t todo, max_todo;
/*
* unlike the get_buffer_out() method, get_buffer_in()
* must return a buffer of at most the given size, see audio.c
*/
max_todo = *size;
todo = self->sndio_pos - self->qemu_pos;
if (todo > max_todo) {
todo = max_todo;
}
*size = todo;
return self->buf + self->qemu_pos;
}
/*
* discard the given amount of recorded data
*/
static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
{
SndioVoice *self = (SndioVoice *) hw;
self->qemu_pos += size;
if (self->qemu_pos == self->buf_size) {
self->qemu_pos = 0;
self->sndio_pos = 0;
}
sndio_poll_wait(self);
}
/*
* call-back called when one of our descriptors becomes writable
*/
static void sndio_poll_out(void *arg)
{
struct pollindex *pindex = (struct pollindex *) arg;
sndio_poll_event(pindex->self, pindex->index, POLLOUT);
}
/*
* call-back called when one of our descriptors becomes readable
*/
static void sndio_poll_in(void *arg)
{
struct pollindex *pindex = (struct pollindex *) arg;
sndio_poll_event(pindex->self, pindex->index, POLLIN);
}
static void sndio_fini(SndioVoice *self)
{
if (self->hdl) {
sio_close(self->hdl);
self->hdl = NULL;
}
g_free(self->pfds);
g_free(self->pindexes);
g_free(self->buf);
}
static int sndio_init(SndioVoice *self,
struct audsettings *as, int mode, Audiodev *dev)
{
AudiodevSndioOptions *opts = &dev->u.sndio;
unsigned long long latency;
const char *dev_name;
struct sio_par req;
unsigned int nch;
int i, nfds;
dev_name = opts->has_dev ? opts->dev : SIO_DEVANY;
latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US;
/* open the device in non-blocking mode */
self->hdl = sio_open(dev_name, mode, 1);
if (self->hdl == NULL) {
dolog("failed to open device\n");
return -1;
}
self->mode = mode;
sio_initpar(&req);
switch (as->fmt) {
case AUDIO_FORMAT_S8:
req.bits = 8;
req.sig = 1;
break;
case AUDIO_FORMAT_U8:
req.bits = 8;
req.sig = 0;
break;
case AUDIO_FORMAT_S16:
req.bits = 16;
req.sig = 1;
break;
case AUDIO_FORMAT_U16:
req.bits = 16;
req.sig = 0;
break;
case AUDIO_FORMAT_S32:
req.bits = 32;
req.sig = 1;
break;
case AUDIO_FORMAT_U32:
req.bits = 32;
req.sig = 0;
break;
default:
dolog("unknown audio sample format\n");
return -1;
}
if (req.bits > 8) {
req.le = as->endianness ? 0 : 1;
}
req.rate = as->freq;
if (mode == SIO_PLAY) {
req.pchan = as->nchannels;
} else {
req.rchan = as->nchannels;
}
/* set on-device buffer size */
req.appbufsz = req.rate * latency / 1000000;
if (!sio_setpar(self->hdl, &req)) {
dolog("failed set audio params\n");
goto fail;
}
if (!sio_getpar(self->hdl, &self->par)) {
dolog("failed get audio params\n");
goto fail;
}
nch = (mode == SIO_PLAY) ? self->par.pchan : self->par.rchan;
/*
* With the default setup, sndio supports any combination of parameters
* so these checks are mostly to catch configuration errors.
*/
if (self->par.bits != req.bits || self->par.bps != req.bits / 8 ||
self->par.sig != req.sig || (req.bits > 8 && self->par.le != req.le) ||
self->par.rate != as->freq || nch != as->nchannels) {
dolog("unsupported audio params\n");
goto fail;
}
/*
* we use one block as buffer size; this is how
* transfers get well aligned
*/
self->buf_size = self->par.round * self->par.bps * nch;
self->buf = g_malloc(self->buf_size);
if (self->buf == NULL) {
dolog("failed to allocate audio buffer\n");
goto fail;
}
nfds = sio_nfds(self->hdl);
self->pfds = g_malloc_n(nfds, sizeof(struct pollfd));
if (self->pfds == NULL) {
dolog("failed to allocate pollfd structures\n");
goto fail;
}
self->pindexes = g_malloc_n(nfds, sizeof(struct pollindex));
if (self->pindexes == NULL) {
dolog("failed to allocate pollindex structures\n");
goto fail;
}
for (i = 0; i < nfds; i++) {
self->pindexes[i].self = self;
self->pindexes[i].index = i;
}
return 0;
fail:
sndio_fini(self);
return -1;
}
static void sndio_enable(SndioVoice *self, bool enable)
{
if (enable) {
sio_start(self->hdl);
self->enabled = true;
sndio_poll_wait(self);
} else {
self->enabled = false;
sndio_poll_clear(self);
sio_stop(self->hdl);
}
}
static void sndio_enable_out(HWVoiceOut *hw, bool enable)
{
SndioVoice *self = (SndioVoice *) hw;
sndio_enable(self, enable);
}
static void sndio_enable_in(HWVoiceIn *hw, bool enable)
{
SndioVoice *self = (SndioVoice *) hw;
sndio_enable(self, enable);
}
static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *opaque)
{
SndioVoice *self = (SndioVoice *) hw;
if (sndio_init(self, as, SIO_PLAY, opaque) == -1) {
return -1;
}
audio_pcm_init_info(&hw->info, as);
hw->samples = self->par.round;
return 0;
}
static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaque)
{
SndioVoice *self = (SndioVoice *) hw;
if (sndio_init(self, as, SIO_REC, opaque) == -1) {
return -1;
}
audio_pcm_init_info(&hw->info, as);
hw->samples = self->par.round;
return 0;
}
static void sndio_fini_out(HWVoiceOut *hw)
{
SndioVoice *self = (SndioVoice *) hw;
sndio_fini(self);
}
static void sndio_fini_in(HWVoiceIn *hw)
{
SndioVoice *self = (SndioVoice *) hw;
sndio_fini(self);
}
static void *sndio_audio_init(Audiodev *dev)
{
assert(dev->driver == AUDIODEV_DRIVER_SNDIO);
return dev;
}
static void sndio_audio_fini(void *opaque)
{
}
static struct audio_pcm_ops sndio_pcm_ops = {
.init_out = sndio_init_out,
.fini_out = sndio_fini_out,
.enable_out = sndio_enable_out,
.write = audio_generic_write,
.buffer_get_free = sndio_buffer_get_free,
.get_buffer_out = sndio_get_buffer_out,
.put_buffer_out = sndio_put_buffer_out,
.init_in = sndio_init_in,
.fini_in = sndio_fini_in,
.read = audio_generic_read,
.enable_in = sndio_enable_in,
.get_buffer_in = sndio_get_buffer_in,
.put_buffer_in = sndio_put_buffer_in,
};
static struct audio_driver sndio_audio_driver = {
.name = "sndio",
.descr = "sndio https://sndio.org",
.init = sndio_audio_init,
.fini = sndio_audio_fini,
.pcm_ops = &sndio_pcm_ops,
.can_be_default = 1,
.max_voices_out = INT_MAX,
.max_voices_in = INT_MAX,
.voice_size_out = sizeof(SndioVoice),
.voice_size_in = sizeof(SndioVoice)
};
static void register_audio_sndio(void)
{
audio_driver_register(&sndio_audio_driver);
}
type_init(register_audio_sndio);

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