Compare commits
70 Commits
migration-
...
v7.2.2
Author | SHA1 | Date | |
---|---|---|---|
|
161e1f22b8 | ||
|
2e0e234272 | ||
|
c221208352 | ||
|
3218890da4 | ||
|
dda57509e9 | ||
|
73a11e3723 | ||
|
b6abbe6250 | ||
|
b57641e907 | ||
|
d9bb73d8e3 | ||
|
c74cba8362 | ||
|
a3f531cee6 | ||
|
5d4218f0b2 | ||
|
248aa3deb6 | ||
|
d8592b05be | ||
|
5d42bf6c57 | ||
|
ad2f459ce0 | ||
|
17b032c659 | ||
|
a3c6cd83e3 | ||
|
a2efa1fac4 | ||
|
b209cc4556 | ||
|
5bfbcc3faa | ||
|
099e8cd9ea | ||
|
32b8913f72 | ||
|
e807a1c29f | ||
|
9d46d348f6 | ||
|
7c8a67ed46 | ||
|
b05232a256 | ||
|
f6d602d078 | ||
|
2529bbf4a7 | ||
|
6cf13d9d01 | ||
|
a7485cdca7 | ||
|
e2672ec498 | ||
|
c21a2456b6 | ||
|
0546b42bce | ||
|
5a1c74097e | ||
|
b8db116da1 | ||
|
b5be595c62 | ||
|
fd4bf2632c | ||
|
b3f36e52ac | ||
|
99fb11df6f | ||
|
814c0b185d | ||
|
8faaaf1bcd | ||
|
851de2751f | ||
|
538c8180c3 | ||
|
1ca37b7ef6 | ||
|
856a67cade | ||
|
f163cf6be4 | ||
|
c45d10f655 | ||
|
6809dbc5c5 | ||
|
8d3c9fc439 | ||
|
93ff84d4c0 | ||
|
2c0fdb4ed3 | ||
|
6e0c910904 | ||
|
eca533b60a | ||
|
ee2ec0ac52 | ||
|
f759e33000 | ||
|
75ecd0872f | ||
|
c3ea5ef558 | ||
|
de605876eb | ||
|
f549ee8c25 | ||
|
4e98327e14 | ||
|
9d86da9e07 | ||
|
6a3aa014c5 | ||
|
e05827b632 | ||
|
6647b6edea | ||
|
305c0f8c54 | ||
|
a2093dd6fe | ||
|
5f43c7786e | ||
|
f16011abc1 | ||
|
e34f86a2f9 |
109
.cirrus.yml
Normal file
109
.cirrus.yml
Normal file
@@ -0,0 +1,109 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
|
||||
windows_msys2_task:
|
||||
timeout_in: 90m
|
||||
windows_container:
|
||||
image: cirrusci/windowsservercore:2019
|
||||
os_version: 2019
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
env:
|
||||
CIRRUS_SHELL: powershell
|
||||
MSYS: winsymlinks:native
|
||||
MSYSTEM: MINGW64
|
||||
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
|
||||
mingw-w64-x86_64-python
|
||||
mingw-w64-x86_64-python-sphinx
|
||||
mingw-w64-x86_64-toolchain
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-SDL2_image
|
||||
mingw-w64-x86_64-gtk3
|
||||
mingw-w64-x86_64-glib2
|
||||
mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-jemalloc
|
||||
mingw-w64-x86_64-lzo2
|
||||
mingw-w64-x86_64-zstd
|
||||
mingw-w64-x86_64-libjpeg-turbo
|
||||
mingw-w64-x86_64-pixman
|
||||
mingw-w64-x86_64-libgcrypt
|
||||
mingw-w64-x86_64-libpng
|
||||
mingw-w64-x86_64-libssh
|
||||
mingw-w64-x86_64-snappy
|
||||
mingw-w64-x86_64-libusb
|
||||
mingw-w64-x86_64-usbredir
|
||||
mingw-w64-x86_64-libtasn1
|
||||
mingw-w64-x86_64-nettle
|
||||
mingw-w64-x86_64-cyrus-sasl
|
||||
mingw-w64-x86_64-curl
|
||||
mingw-w64-x86_64-gnutls
|
||||
mingw-w64-x86_64-libnfs
|
||||
"
|
||||
CHERE_INVOKING: 1
|
||||
msys2_cache:
|
||||
folder: C:\tools\archive
|
||||
reupload_on_changes: false
|
||||
# These env variables are used to generate fingerprint to trigger the cache procedure
|
||||
# If wanna to force re-populate msys2, increase MSYS2_FINGERPRINT
|
||||
fingerprint_script:
|
||||
- |
|
||||
echo $env:CIRRUS_TASK_NAME
|
||||
echo $env:MSYS2_URL
|
||||
echo $env:MSYS2_FINGERPRINT
|
||||
echo $env:MSYS2_PACKAGES
|
||||
populate_script:
|
||||
- |
|
||||
md -Force C:\tools\archive\pkg
|
||||
$start_time = Get-Date
|
||||
bitsadmin /transfer msys_download /dynamic /download /priority FOREGROUND $env:MSYS2_URL C:\tools\archive\base.exe
|
||||
Write-Output "Download time taken: $((Get-Date).Subtract($start_time))"
|
||||
cd C:\tools
|
||||
C:\tools\archive\base.exe -y
|
||||
del -Force C:\tools\archive\base.exe
|
||||
Write-Output "Base install time taken: $((Get-Date).Subtract($start_time))"
|
||||
$start_time = Get-Date
|
||||
|
||||
((Get-Content -path C:\tools\msys64\etc\\post-install\\07-pacman-key.post -Raw) -replace '--refresh-keys', '--version') | Set-Content -Path C:\tools\msys64\etc\\post-install\\07-pacman-key.post
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf"
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "export"
|
||||
C:\tools\msys64\usr\bin\pacman.exe --noconfirm -Sy
|
||||
echo Y | C:\tools\msys64\usr\bin\pacman.exe --noconfirm -Suu --overwrite=*
|
||||
taskkill /F /FI "MODULES eq msys-2.0.dll"
|
||||
tasklist
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "mv -f /etc/pacman.conf.pacnew /etc/pacman.conf || true"
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu --overwrite=*"
|
||||
Write-Output "Core install time taken: $((Get-Date).Subtract($start_time))"
|
||||
$start_time = Get-Date
|
||||
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -S --needed $env:MSYS2_PACKAGES"
|
||||
Write-Output "Package install time taken: $((Get-Date).Subtract($start_time))"
|
||||
$start_time = Get-Date
|
||||
|
||||
del -Force -ErrorAction SilentlyContinue C:\tools\msys64\etc\mtab
|
||||
del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\fd
|
||||
del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stderr
|
||||
del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stdin
|
||||
del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stdout
|
||||
del -Force -Recurse -ErrorAction SilentlyContinue C:\tools\msys64\var\cache\pacman\pkg
|
||||
tar cf C:\tools\archive\msys64.tar -C C:\tools\ msys64
|
||||
|
||||
Write-Output "Package archive time taken: $((Get-Date).Subtract($start_time))"
|
||||
del -Force -Recurse -ErrorAction SilentlyContinue c:\tools\msys64
|
||||
install_script:
|
||||
- |
|
||||
$start_time = Get-Date
|
||||
cd C:\tools
|
||||
ls C:\tools\archive\msys64.tar
|
||||
tar xf C:\tools\archive\msys64.tar
|
||||
Write-Output "Extract msys2 time taken: $((Get-Date).Subtract($start_time))"
|
||||
script:
|
||||
- C:\tools\msys64\usr\bin\bash.exe -lc "mkdir build"
|
||||
- C:\tools\msys64\usr\bin\bash.exe -lc "cd build && ../configure --python=python3"
|
||||
- C:\tools\msys64\usr\bin\bash.exe -lc "cd build && make -j8"
|
||||
- exit $LastExitCode
|
||||
test_script:
|
||||
- C:\tools\msys64\usr\bin\bash.exe -lc "cd build && make V=1 check"
|
||||
- exit $LastExitCode
|
@@ -1,21 +0,0 @@
|
||||
#
|
||||
# List of code-formatting clean ups the git blame can ignore
|
||||
#
|
||||
# git blame --ignore-revs-file .git-blame-ignore-revs
|
||||
#
|
||||
# or
|
||||
#
|
||||
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
#
|
||||
|
||||
# gdbstub: clean-up indents
|
||||
ad9e4585b3c7425759d3eea697afbca71d2c2082
|
||||
|
||||
# e1000e: fix code style
|
||||
0eadd56bf53ab196a16d492d7dd31c62e1c24c32
|
||||
|
||||
# target/riscv: coding style fixes
|
||||
8c7feddddd9218b407792120bcfda0347ed16205
|
||||
|
||||
# replace TABs with spaces
|
||||
48805df9c22a0700fba4b3b548fafaa21726ca68
|
@@ -1,68 +1,39 @@
|
||||
|
||||
variables:
|
||||
# On stable branches this is changed by later rules. Should also
|
||||
# be overridden per pipeline if running pipelines concurrently
|
||||
# for different branches in contributor forks.
|
||||
QEMU_CI_CONTAINER_TAG: latest
|
||||
|
||||
# For purposes of CI rules, upstream is the gitlab.com/qemu-project
|
||||
# namespace. When testing CI, it might be usefult to override this
|
||||
# to point to a fork repo
|
||||
QEMU_CI_UPSTREAM: qemu-project
|
||||
|
||||
# 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
|
||||
#
|
||||
# For pipelines running for stable "staging-X.Y" branches
|
||||
# we must override QEMU_CI_CONTAINER_TAG
|
||||
#
|
||||
.base_job_template:
|
||||
variables:
|
||||
# Each script line from will be in a collapsible section in the job output
|
||||
# and show the duration of each line.
|
||||
FF_SCRIPT_SECTIONS: 1
|
||||
|
||||
interruptible: true
|
||||
|
||||
rules:
|
||||
#############################################################
|
||||
# Stage 1: exclude scenarios where we definitely don't
|
||||
# want jobs to run
|
||||
#############################################################
|
||||
|
||||
# Never run jobs upstream on stable branch, staging branch jobs already ran
|
||||
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /^stable-/'
|
||||
when: never
|
||||
|
||||
# Never run jobs upstream on tags, staging branch jobs already ran
|
||||
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG'
|
||||
when: never
|
||||
|
||||
# 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_CI_UPSTREAM && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
|
||||
- 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_CI_UPSTREAM && $CI_COMMIT_BRANCH !~ /staging/'
|
||||
- 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_CI_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_CI_UPSTREAM'
|
||||
- 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_CI_UPSTREAM'
|
||||
- if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != "qemu-project"'
|
||||
when: never
|
||||
|
||||
|
||||
@@ -72,29 +43,17 @@ variables:
|
||||
#############################################################
|
||||
|
||||
# Optional jobs should not be run unless manually triggered
|
||||
- if: '$QEMU_JOB_OPTIONAL && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
variables:
|
||||
QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
|
||||
|
||||
- if: '$QEMU_JOB_OPTIONAL'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
# Skipped jobs should not be run unless manually triggered
|
||||
- if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
variables:
|
||||
QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
|
||||
|
||||
- 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_CI_UPSTREAM'
|
||||
- if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != "qemu-project"'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
@@ -106,23 +65,8 @@ variables:
|
||||
|
||||
# Forks pipeline jobs don't start automatically unless
|
||||
# QEMU_CI=2 is set
|
||||
- if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
|
||||
- if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"'
|
||||
when: manual
|
||||
|
||||
# Upstream pipeline jobs start automatically unless told not to
|
||||
# by setting QEMU_CI=1
|
||||
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
|
||||
when: manual
|
||||
variables:
|
||||
QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
|
||||
|
||||
- if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM'
|
||||
when: manual
|
||||
|
||||
# Jobs can run if any jobs they depend on were successful
|
||||
- if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
|
||||
when: on_success
|
||||
variables:
|
||||
QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
|
||||
|
||||
# Jobs can run if any jobs they depend on were successfull
|
||||
- when: on_success
|
||||
|
@@ -1,19 +1,25 @@
|
||||
.native_build_job_template:
|
||||
extends: .base_job_template
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
before_script:
|
||||
- JOBS=$(expr $(nproc) + 1)
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-werror --disable-docs --enable-fdt=system
|
||||
${TARGETS:+--target-list="$TARGETS"}
|
||||
$CONFIGURE_ARGS ||
|
||||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- if test -n "$LD_JOBS";
|
||||
then
|
||||
pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||
scripts/git-submodule.sh update meson ;
|
||||
fi
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if test -n "$TARGETS";
|
||||
then
|
||||
../configure --enable-werror --disable-docs ${LD_JOBS:+--meson=git} $CONFIGURE_ARGS --target-list="$TARGETS" ;
|
||||
else
|
||||
../configure --enable-werror --disable-docs ${LD_JOBS:+--meson=git} $CONFIGURE_ARGS ;
|
||||
fi || { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- if test -n "$LD_JOBS";
|
||||
then
|
||||
../meson/meson.py configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||
fi || exit 1;
|
||||
- make -j"$JOBS"
|
||||
- if test -n "$MAKE_CHECK_ARGS";
|
||||
@@ -21,29 +27,13 @@
|
||||
make -j"$JOBS" $MAKE_CHECK_ARGS ;
|
||||
fi
|
||||
|
||||
# We jump some hoops in common_test_job_template to avoid
|
||||
# rebuilding all the object files we skip in the artifacts
|
||||
.native_build_artifact_template:
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
- .git-submodule-status
|
||||
exclude:
|
||||
- build/**/*.p
|
||||
- build/**/*.a.p
|
||||
- build/**/*.fa.p
|
||||
- build/**/*.c.o
|
||||
- build/**/*.c.o.d
|
||||
- build/**/*.fa
|
||||
|
||||
.common_test_job_template:
|
||||
extends: .base_job_template
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- scripts/git-submodule.sh update roms/SLOF
|
||||
- meson subprojects download $(cd build/subprojects && echo *)
|
||||
- scripts/git-submodule.sh update
|
||||
$(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak)
|
||||
- cd build
|
||||
- find . -type f -exec touch {} +
|
||||
# Avoid recompiling by hiding ninja with NINJA=":"
|
||||
|
@@ -2,16 +2,20 @@ include:
|
||||
- local: '/.gitlab-ci.d/buildtest-template.yml'
|
||||
|
||||
build-system-alpine:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-alpine-container
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
TARGETS: avr-softmmu loongarch64-softmmu mips64-softmmu mipsel-softmmu
|
||||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
CONFIGURE_ARGS: --enable-docs --enable-trace-backends=log,simple,syslog
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- .git-submodule-status
|
||||
- build
|
||||
|
||||
check-system-alpine:
|
||||
extends: .native_test_job_template
|
||||
@@ -32,17 +36,19 @@ avocado-system-alpine:
|
||||
MAKE_CHECK_ARGS: check-avocado
|
||||
|
||||
build-system-ubuntu:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2204-container
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2204
|
||||
CONFIGURE_ARGS: --enable-docs
|
||||
TARGETS: alpha-softmmu cris-softmmu hppa-softmmu
|
||||
IMAGE: ubuntu2004
|
||||
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
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-ubuntu:
|
||||
extends: .native_test_job_template
|
||||
@@ -50,7 +56,7 @@ check-system-ubuntu:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2204
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
avocado-system-ubuntu:
|
||||
@@ -59,21 +65,22 @@ avocado-system-ubuntu:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2204
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check-avocado
|
||||
|
||||
build-system-debian:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --with-coroutine=sigaltstack
|
||||
TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu
|
||||
sparc-softmmu xtensaeb-softmmu
|
||||
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
|
||||
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-debian:
|
||||
extends: .native_test_job_template
|
||||
@@ -102,21 +109,24 @@ crash-test-debian:
|
||||
IMAGE: debian-amd64
|
||||
script:
|
||||
- cd build
|
||||
- make NINJA=":" check-venv
|
||||
- tests/venv/bin/python3 scripts/device-crash-test -q --tcg-only ./qemu-system-i386
|
||||
- make check-venv
|
||||
- tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-i386
|
||||
|
||||
build-system-fedora:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs
|
||||
--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
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-fedora:
|
||||
extends: .native_test_job_template
|
||||
@@ -145,23 +155,26 @@ crash-test-fedora:
|
||||
IMAGE: fedora
|
||||
script:
|
||||
- cd build
|
||||
- make NINJA=":" check-venv
|
||||
- make 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
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system
|
||||
--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
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-centos:
|
||||
extends: .native_test_job_template
|
||||
@@ -182,15 +195,18 @@ avocado-system-centos:
|
||||
MAKE_CHECK_ARGS: check-avocado
|
||||
|
||||
build-system-opensuse:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-opensuse-leap-container
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
CONFIGURE_ARGS: --enable-fdt=system
|
||||
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-opensuse:
|
||||
extends: .native_test_job_template
|
||||
@@ -228,7 +244,6 @@ build-tcg-disabled:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-tcg --audio-drv-list="" --with-coroutine=ucontext
|
||||
--disable-docs --disable-sdl --disable-gtk --disable-vnc
|
||||
|| { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- make -j"$JOBS"
|
||||
- make check-unit
|
||||
@@ -260,10 +275,14 @@ build-user-static:
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system --static
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Because the hexagon cross-compiler takes so long to build we don't rely
|
||||
# on the CI system to build it and hence this job has an optional dependency
|
||||
# declared. The image is manually uploaded.
|
||||
build-user-hexagon:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: hexagon-cross-container
|
||||
optional: true
|
||||
variables:
|
||||
IMAGE: debian-hexagon-cross
|
||||
TARGETS: hexagon-linux-user
|
||||
@@ -300,7 +319,8 @@ clang-system:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++
|
||||
--extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined
|
||||
TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu s390x-softmmu
|
||||
TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||
ppc-softmmu s390x-softmmu
|
||||
MAKE_CHECK_ARGS: check-qtest check-tcg
|
||||
|
||||
clang-user:
|
||||
@@ -325,9 +345,7 @@ clang-user:
|
||||
# Split in three sets of build/check/avocado to limit the execution time of each
|
||||
# job
|
||||
build-cfi-aarch64:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
@@ -343,6 +361,10 @@ build-cfi-aarch64:
|
||||
# skipped until the situation has been solved.
|
||||
QEMU_JOB_SKIPPED: 1
|
||||
timeout: 90m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-aarch64:
|
||||
extends: .native_test_job_template
|
||||
@@ -363,9 +385,7 @@ avocado-cfi-aarch64:
|
||||
MAKE_CHECK_ARGS: check-avocado
|
||||
|
||||
build-cfi-ppc64-s390x:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
@@ -381,6 +401,10 @@ build-cfi-ppc64-s390x:
|
||||
# skipped until the situation has been solved.
|
||||
QEMU_JOB_SKIPPED: 1
|
||||
timeout: 80m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-ppc64-s390x:
|
||||
extends: .native_test_job_template
|
||||
@@ -401,9 +425,7 @@ avocado-cfi-ppc64-s390x:
|
||||
MAKE_CHECK_ARGS: check-avocado
|
||||
|
||||
build-cfi-x86_64:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
@@ -415,6 +437,10 @@ build-cfi-x86_64:
|
||||
TARGETS: x86_64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-x86_64:
|
||||
extends: .native_test_job_template
|
||||
@@ -437,23 +463,35 @@ avocado-cfi-x86_64:
|
||||
tsan-build:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2204-container
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2204
|
||||
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
|
||||
--enable-trace-backends=ust --disable-slirp
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-tsan --cc=clang-10 --cxx=clang++-10
|
||||
--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
|
||||
|
||||
# gcov is a GCC features
|
||||
gcov:
|
||||
# gprof/gcov are GCC features
|
||||
build-gprof-gcov:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2204-container
|
||||
timeout: 80m
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2204
|
||||
CONFIGURE_ARGS: --enable-gcov
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-gprof --enable-gcov
|
||||
TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu
|
||||
artifacts:
|
||||
expire_in: 1 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-gprof-gcov:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-gprof-gcov
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check
|
||||
after_script:
|
||||
- cd build
|
||||
@@ -476,7 +514,6 @@ build-oss-fuzz:
|
||||
IMAGE: fedora
|
||||
script:
|
||||
- mkdir build-oss-fuzz
|
||||
- export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt
|
||||
- CC="clang" CXX="clang++" CFLAGS="-fsanitize=address"
|
||||
./scripts/oss-fuzz/build.sh
|
||||
- export ASAN_OPTIONS="fast_unwind_on_malloc=0"
|
||||
@@ -497,9 +534,8 @@ build-tci:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze ppc64 s390x x86_64"
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-tcg-interpreter --disable-docs --disable-gtk --disable-vnc
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
||||
|| { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- ../configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- make -j"$JOBS"
|
||||
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
@@ -511,28 +547,47 @@ build-tci:
|
||||
- QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow
|
||||
- make check-tcg
|
||||
|
||||
# Alternate coroutines implementations are only really of interest to KVM users
|
||||
# However we can't test against KVM on Gitlab-CI so we can only run unit tests
|
||||
build-coroutine-sigaltstack:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --with-coroutine=sigaltstack --disable-tcg
|
||||
--enable-trace-backends=ftrace
|
||||
MAKE_CHECK_ARGS: check-unit
|
||||
|
||||
# Check our reduced build configurations
|
||||
build-without-defaults:
|
||||
build-without-default-devices:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --without-default-devices --disable-user
|
||||
|
||||
build-without-default-features:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS:
|
||||
--without-default-devices
|
||||
--without-default-features
|
||||
--disable-fdt
|
||||
--disable-capstone
|
||||
--disable-pie
|
||||
--disable-qom-cast-debug
|
||||
--disable-strip
|
||||
TARGETS: avr-softmmu mips64-softmmu s390x-softmmu sh4-softmmu
|
||||
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
|
||||
MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow
|
||||
|
||||
build-libvhost-user:
|
||||
extends: .base_job_template
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
script:
|
||||
@@ -544,9 +599,7 @@ build-libvhost-user:
|
||||
# No targets are built here, just tools, docs, and unit tests. This
|
||||
# also feeds into the eventual documentation deployment steps later
|
||||
build-tools-and-docs-debian:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
# when running on 'master' we use pre-existing container
|
||||
@@ -556,6 +609,10 @@ build-tools-and-docs-debian:
|
||||
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:
|
||||
- build
|
||||
|
||||
# Prepare for GitLab pages deployment. Anything copied into the
|
||||
# "public" directory will be deployed to $USER.gitlab.io/$PROJECT
|
||||
@@ -572,7 +629,7 @@ build-tools-and-docs-debian:
|
||||
# of what topic branch they're currently using
|
||||
pages:
|
||||
extends: .base_job_template
|
||||
image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest
|
||||
stage: test
|
||||
needs:
|
||||
- job: build-tools-and-docs-debian
|
||||
|
@@ -44,6 +44,19 @@
|
||||
variables:
|
||||
QEMU_JOB_CIRRUS: 1
|
||||
|
||||
x64-freebsd-12-build:
|
||||
extends: .cirrus_build_job
|
||||
variables:
|
||||
NAME: freebsd-12
|
||||
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
|
||||
CIRRUS_VM_IMAGE_SELECTOR: image_family
|
||||
CIRRUS_VM_IMAGE_NAME: freebsd-12-3
|
||||
CIRRUS_VM_CPUS: 8
|
||||
CIRRUS_VM_RAM: 8G
|
||||
UPDATE_COMMAND: pkg update
|
||||
INSTALL_COMMAND: pkg install -y
|
||||
TEST_TARGETS: check
|
||||
|
||||
x64-freebsd-13-build:
|
||||
extends: .cirrus_build_job
|
||||
variables:
|
||||
@@ -53,7 +66,7 @@ x64-freebsd-13-build:
|
||||
CIRRUS_VM_IMAGE_NAME: freebsd-13-1
|
||||
CIRRUS_VM_CPUS: 8
|
||||
CIRRUS_VM_RAM: 8G
|
||||
UPDATE_COMMAND: pkg update; pkg upgrade -y
|
||||
UPDATE_COMMAND: pkg update
|
||||
INSTALL_COMMAND: pkg install -y
|
||||
TEST_TARGETS: check
|
||||
|
||||
|
@@ -32,9 +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
|
||||
always:
|
||||
build_result_artifacts:
|
||||
path: build/meson-logs/*log.txt
|
||||
type: text/plain
|
||||
|
16
.gitlab-ci.d/cirrus/freebsd-12.vars
Normal file
16
.gitlab-ci.d/cirrus/freebsd-12.vars
Normal file
@@ -0,0 +1,16 @@
|
||||
# THIS FILE WAS AUTO-GENERATED
|
||||
#
|
||||
# $ lcitool variables freebsd-12 qemu
|
||||
#
|
||||
# https://gitlab.com/libvirt/libvirt-ci
|
||||
|
||||
CCACHE='/usr/local/bin/ccache'
|
||||
CPAN_PKGS=''
|
||||
CROSS_PKGS=''
|
||||
MAKE='/usr/local/bin/gmake'
|
||||
NINJA='/usr/local/bin/ninja'
|
||||
PACKAGING_COMMAND='pkg'
|
||||
PIP3='/usr/local/bin/pip-3.8'
|
||||
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'
|
@@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake'
|
||||
NINJA='/usr/local/bin/ninja'
|
||||
PACKAGING_COMMAND='pkg'
|
||||
PIP3='/usr/local/bin/pip-3.8'
|
||||
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache 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 mtools ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso 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'
|
||||
|
@@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake'
|
||||
NINJA='/opt/homebrew/bin/ninja'
|
||||
PACKAGING_COMMAND='brew'
|
||||
PIP3='/opt/homebrew/bin/pip3'
|
||||
PKGS='bash bc 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 mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol tesseract usbredir vde vte3 xorriso zlib zstd'
|
||||
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'
|
||||
|
@@ -34,11 +34,31 @@ armhf-debian-cross-container:
|
||||
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: .container_job_template
|
||||
extends: .base_job_template
|
||||
image: docker:stable
|
||||
stage: containers
|
||||
variables:
|
||||
NAME: debian-hexagon-cross
|
||||
GIT_DEPTH: 1
|
||||
QEMU_JOB_ONLY_FORKS: 1
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
|
||||
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest"
|
||||
- docker info
|
||||
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||
script:
|
||||
- echo "TAG:$TAG"
|
||||
- echo "COMMON_TAG:$COMMON_TAG"
|
||||
- docker pull $COMMON_TAG
|
||||
- docker tag $COMMON_TAG $TAG
|
||||
- docker push "$TAG"
|
||||
after_script:
|
||||
- docker logout
|
||||
|
||||
hppa-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
|
@@ -1,21 +1,22 @@
|
||||
.container_job_template:
|
||||
extends: .base_job_template
|
||||
image: docker:latest
|
||||
image: docker:stable
|
||||
stage: containers
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:$QEMU_CI_CONTAINER_TAG"
|
||||
# Always ':latest' because we always use upstream as a common cache source
|
||||
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest"
|
||||
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
|
||||
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/$NAME:latest"
|
||||
- apk add python3
|
||||
- docker info
|
||||
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||
- until docker info; do sleep 1; done
|
||||
script:
|
||||
- echo "TAG:$TAG"
|
||||
- echo "COMMON_TAG:$COMMON_TAG"
|
||||
- docker build --tag "$TAG" --cache-from "$TAG" --cache-from "$COMMON_TAG"
|
||||
--build-arg BUILDKIT_INLINE_CACHE=1
|
||||
-f "tests/docker/dockerfiles/$NAME.docker" "."
|
||||
- ./tests/docker/docker.py --engine docker build
|
||||
-t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
|
||||
-r $CI_REGISTRY/qemu-project/qemu
|
||||
- docker tag "qemu/$NAME" "$TAG"
|
||||
- docker push "$TAG"
|
||||
after_script:
|
||||
- docker logout
|
||||
|
@@ -13,10 +13,10 @@ amd64-debian-container:
|
||||
variables:
|
||||
NAME: debian-amd64
|
||||
|
||||
amd64-ubuntu2204-container:
|
||||
amd64-ubuntu2004-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: ubuntu2204
|
||||
NAME: ubuntu2004
|
||||
|
||||
amd64-opensuse-leap-container:
|
||||
extends: .container_job_template
|
||||
|
@@ -1,14 +1,14 @@
|
||||
.cross_system_build_job:
|
||||
extends: .base_job_template
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
timeout: 80m
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-werror --disable-docs --enable-fdt=system
|
||||
--disable-user $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS
|
||||
--target-list-exclude="arm-softmmu cris-softmmu
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-user --target-list-exclude="arm-softmmu cris-softmmu
|
||||
i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu
|
||||
mips64-softmmu ppc-softmmu riscv32-softmmu sh4-softmmu
|
||||
sparc-softmmu xtensa-softmmu $CROSS_SKIP_TARGETS"
|
||||
@@ -27,36 +27,27 @@
|
||||
.cross_accel_build_job:
|
||||
extends: .base_job_template
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
timeout: 30m
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-tools --enable-${ACCEL:-kvm} $EXTRA_CONFIGURE_OPTS
|
||||
- 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:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--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
|
||||
|
||||
# We can still run some tests on some of our cross build jobs. They can add this
|
||||
# template to their extends to save the build logs and test results
|
||||
.cross_test_artifacts:
|
||||
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
|
||||
|
@@ -1,6 +1,13 @@
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/crossbuild-template.yml'
|
||||
|
||||
cross-armel-system:
|
||||
extends: .cross_system_build_job
|
||||
needs:
|
||||
job: armel-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-armel-cross
|
||||
|
||||
cross-armel-user:
|
||||
extends: .cross_user_build_job
|
||||
needs:
|
||||
@@ -8,6 +15,13 @@ cross-armel-user:
|
||||
variables:
|
||||
IMAGE: debian-armel-cross
|
||||
|
||||
cross-armhf-system:
|
||||
extends: .cross_system_build_job
|
||||
needs:
|
||||
job: armhf-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-armhf-cross
|
||||
|
||||
cross-armhf-user:
|
||||
extends: .cross_user_build_job
|
||||
needs:
|
||||
@@ -29,18 +43,16 @@ cross-arm64-user:
|
||||
variables:
|
||||
IMAGE: debian-arm64-cross
|
||||
|
||||
cross-arm64-kvm-only:
|
||||
extends: .cross_accel_build_job
|
||||
cross-i386-system:
|
||||
extends: .cross_system_build_job
|
||||
needs:
|
||||
job: arm64-debian-cross-container
|
||||
job: i386-fedora-cross-container
|
||||
variables:
|
||||
IMAGE: debian-arm64-cross
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features
|
||||
IMAGE: fedora-i386-cross
|
||||
MAKE_CHECK_ARGS: check-qtest
|
||||
|
||||
cross-i386-user:
|
||||
extends:
|
||||
- .cross_user_build_job
|
||||
- .cross_test_artifacts
|
||||
extends: .cross_user_build_job
|
||||
needs:
|
||||
job: i386-fedora-cross-container
|
||||
variables:
|
||||
@@ -48,9 +60,7 @@ cross-i386-user:
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
cross-i386-tci:
|
||||
extends:
|
||||
- .cross_accel_build_job
|
||||
- .cross_test_artifacts
|
||||
extends: .cross_accel_build_job
|
||||
timeout: 60m
|
||||
needs:
|
||||
job: i386-fedora-cross-container
|
||||
@@ -102,14 +112,6 @@ cross-ppc64el-user:
|
||||
variables:
|
||||
IMAGE: debian-ppc64el-cross
|
||||
|
||||
cross-ppc64el-kvm-only:
|
||||
extends: .cross_accel_build_job
|
||||
needs:
|
||||
job: ppc64el-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-ppc64el-cross
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
|
||||
|
||||
# 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.
|
||||
@@ -149,7 +151,7 @@ cross-s390x-kvm-only:
|
||||
job: s390x-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-s390x-cross
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg
|
||||
|
||||
cross-mips64el-kvm-only:
|
||||
extends: .cross_accel_build_job
|
||||
@@ -165,7 +167,6 @@ cross-win32-system:
|
||||
job: win32-fedora-cross-container
|
||||
variables:
|
||||
IMAGE: fedora-win32-cross
|
||||
EXTRA_CONFIGURE_OPTS: --enable-fdt=internal
|
||||
CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu
|
||||
microblazeel-softmmu mips64el-softmmu nios2-softmmu
|
||||
artifacts:
|
||||
@@ -178,10 +179,7 @@ cross-win64-system:
|
||||
job: win64-fedora-cross-container
|
||||
variables:
|
||||
IMAGE: fedora-win64-cross
|
||||
EXTRA_CONFIGURE_OPTS: --enable-fdt=internal
|
||||
CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu
|
||||
m68k-softmmu microblazeel-softmmu nios2-softmmu
|
||||
or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu
|
||||
CROSS_SKIP_TARGETS: or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu
|
||||
tricore-softmmu xtensaeb-softmmu
|
||||
artifacts:
|
||||
paths:
|
||||
|
@@ -13,20 +13,6 @@
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
|
||||
# All custom runners can extend this template to upload the testlog
|
||||
# data as an artifact and also feed the junit report
|
||||
.custom_runner_template:
|
||||
extends: .base_job_template
|
||||
artifacts:
|
||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||
expire_in: 7 days
|
||||
when: always
|
||||
paths:
|
||||
- build/build.ninja
|
||||
- build/meson-logs
|
||||
reports:
|
||||
junit: build/meson-logs/testlog.junit.xml
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml'
|
||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
|
||||
|
@@ -1,9 +1,4 @@
|
||||
# All centos-stream-8 jobs should run successfully in an environment
|
||||
# setup by the scripts/ci/setup/stream/8/build-environment.yml task
|
||||
# "Installation of extra packages to build QEMU"
|
||||
|
||||
centos-stream-8-x86_64:
|
||||
extends: .custom_runner_template
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
@@ -13,6 +8,15 @@ 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:
|
||||
@@ -21,4 +25,6 @@ centos-stream-8-x86_64:
|
||||
- ../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 check-avocado
|
||||
- make NINJA=":" check
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
- ../scripts/ci/org.centos/stream/8/x86_64/test-avocado
|
||||
|
@@ -3,7 +3,6 @@
|
||||
# "Install basic packages to build QEMU on Ubuntu 20.04/20.04"
|
||||
|
||||
ubuntu-20.04-s390x-all-linux-static:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -20,11 +19,12 @@ ubuntu-20.04-s390x-all-linux-static:
|
||||
- ../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 check-tcg
|
||||
- make --output-sync -j`nproc` check
|
||||
- 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:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -40,10 +40,10 @@ ubuntu-20.04-s390x-all:
|
||||
- ../configure --disable-libssh
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
||||
ubuntu-20.04-s390x-alldbg:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -63,10 +63,10 @@ ubuntu-20.04-s390x-alldbg:
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- make clean
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
||||
ubuntu-20.04-s390x-clang:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -85,7 +85,8 @@ ubuntu-20.04-s390x-clang:
|
||||
- ../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
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
||||
ubuntu-20.04-s390x-tci:
|
||||
needs: []
|
||||
@@ -108,7 +109,6 @@ ubuntu-20.04-s390x-tci:
|
||||
- make --output-sync -j`nproc`
|
||||
|
||||
ubuntu-20.04-s390x-notcg:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -127,4 +127,5 @@ ubuntu-20.04-s390x-notcg:
|
||||
- ../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
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
@@ -1,9 +1,8 @@
|
||||
# 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 22.04"
|
||||
# "Install basic packages to build QEMU on Ubuntu 20.04"
|
||||
|
||||
ubuntu-22.04-aarch32-all:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -22,4 +21,5 @@ ubuntu-22.04-aarch32-all:
|
||||
- ../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
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
@@ -1,9 +1,8 @@
|
||||
# All ubuntu-22.04 jobs should run successfully in an environment
|
||||
# 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 22.04"
|
||||
# "Install basic packages to build QEMU on Ubuntu 20.04"
|
||||
|
||||
ubuntu-22.04-aarch64-all-linux-static:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -20,11 +19,12 @@ ubuntu-22.04-aarch64-all-linux-static:
|
||||
- ../configure --enable-debug --static --disable-system --disable-pie
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make check-tcg
|
||||
- make --output-sync -j`nproc --ignore=40` check
|
||||
- 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:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -43,32 +43,10 @@ ubuntu-22.04-aarch64-all:
|
||||
- ../configure
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check
|
||||
|
||||
ubuntu-22.04-aarch64-without-defaults:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
allow_failure: true
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-user --without-default-devices --without-default-features
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
||||
ubuntu-22.04-aarch64-alldbg:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -84,10 +62,10 @@ ubuntu-22.04-aarch64-alldbg:
|
||||
|| { 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
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
||||
ubuntu-22.04-aarch64-clang:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -103,10 +81,11 @@ ubuntu-22.04-aarch64-clang:
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers
|
||||
- ../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
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
||||
ubuntu-22.04-aarch64-tci:
|
||||
needs: []
|
||||
@@ -129,7 +108,6 @@ ubuntu-22.04-aarch64-tci:
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
|
||||
ubuntu-22.04-aarch64-notcg:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
@@ -145,7 +123,8 @@ ubuntu-22.04-aarch64-notcg:
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-tcg --with-devices-aarch64=minimal
|
||||
- ../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
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|| { cat meson-logs/testlog.txt; exit 1; } ;
|
||||
|
85
.gitlab-ci.d/edk2.yml
Normal file
85
.gitlab-ci.d/edk2.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
# All jobs needing docker-edk2 must use the same rules it uses.
|
||||
.edk2_job_rules:
|
||||
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
|
||||
|
||||
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 --
|
||||
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
|
27
.gitlab-ci.d/edk2/Dockerfile
Normal file
27
.gitlab-ci.d/edk2/Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Docker image to cross-compile EDK2 firmware binaries
|
||||
#
|
||||
FROM ubuntu:18.04
|
||||
|
||||
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
|
||||
# Install packages required to build EDK2
|
||||
RUN apt update \
|
||||
&& \
|
||||
\
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
apt install --assume-yes --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
dos2unix \
|
||||
gcc-aarch64-linux-gnu \
|
||||
gcc-arm-linux-gnueabi \
|
||||
git \
|
||||
iasl \
|
||||
make \
|
||||
nasm \
|
||||
python3 \
|
||||
uuid-dev \
|
||||
&& \
|
||||
\
|
||||
rm -rf /var/lib/apt/lists/*
|
@@ -42,15 +42,17 @@
|
||||
docker-opensbi:
|
||||
extends: .opensbi_job_rules
|
||||
stage: containers
|
||||
image: docker:latest
|
||||
image: docker:19.03.1
|
||||
services:
|
||||
- docker:dind
|
||||
- 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
|
||||
- until docker info; do sleep 1; done
|
||||
script:
|
||||
- docker pull $IMAGE_TAG || true
|
||||
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
|
@@ -15,7 +15,6 @@ RUN apt update \
|
||||
ca-certificates \
|
||||
git \
|
||||
make \
|
||||
python3 \
|
||||
wget \
|
||||
&& \
|
||||
\
|
||||
|
@@ -1,16 +1,10 @@
|
||||
# This file contains the set of jobs run by the QEMU project:
|
||||
# https://gitlab.com/qemu-project/qemu/-/pipelines
|
||||
|
||||
variables:
|
||||
RUNNER_TAG: ""
|
||||
|
||||
default:
|
||||
tags:
|
||||
- $RUNNER_TAG
|
||||
|
||||
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'
|
||||
- local: '/.gitlab-ci.d/containers.yml'
|
||||
- local: '/.gitlab-ci.d/crossbuilds.yml'
|
||||
|
@@ -23,12 +23,12 @@ check-dco:
|
||||
before_script:
|
||||
- apk -U add git
|
||||
|
||||
check-python-minreqs:
|
||||
check-python-pipenv:
|
||||
extends: .base_job_template
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/python:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/python:latest
|
||||
script:
|
||||
- make -C python check-minreqs
|
||||
- make -C python check-pipenv
|
||||
variables:
|
||||
GIT_DEPTH: 1
|
||||
needs:
|
||||
@@ -37,7 +37,7 @@ check-python-minreqs:
|
||||
check-python-tox:
|
||||
extends: .base_job_template
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/python:$QEMU_CI_CONTAINER_TAG
|
||||
image: $CI_REGISTRY_IMAGE/qemu/python:latest
|
||||
script:
|
||||
- make -C python check-tox
|
||||
variables:
|
||||
|
@@ -10,14 +10,7 @@
|
||||
- ${CI_PROJECT_DIR}/msys64/var/cache
|
||||
needs: []
|
||||
stage: build
|
||||
timeout: 80m
|
||||
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"
|
||||
timeout: 70m
|
||||
before_script:
|
||||
- If ( !(Test-Path -Path msys64\var\cache ) ) {
|
||||
mkdir msys64\var\cache
|
||||
@@ -45,19 +38,14 @@ msys2-64bit:
|
||||
mingw-w64-x86_64-capstone
|
||||
mingw-w64-x86_64-curl
|
||||
mingw-w64-x86_64-cyrus-sasl
|
||||
mingw-w64-x86_64-dtc
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-glib2
|
||||
mingw-w64-x86_64-gnutls
|
||||
mingw-w64-x86_64-gtk3
|
||||
mingw-w64-x86_64-libgcrypt
|
||||
mingw-w64-x86_64-libjpeg-turbo
|
||||
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-lzo2
|
||||
mingw-w64-x86_64-nettle
|
||||
mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-pixman
|
||||
@@ -66,25 +54,15 @@ msys2-64bit:
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-SDL2_image
|
||||
mingw-w64-x86_64-snappy
|
||||
mingw-w64-x86_64-spice
|
||||
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:MSYSTEM = 'MINGW64' # Start a 64 bit Mingw environment
|
||||
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
|
||||
- mkdir build
|
||||
- cd build
|
||||
# Note: do not remove "--without-default-devices"!
|
||||
# commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices"
|
||||
# changed to compile QEMU with the --without-default-devices switch
|
||||
# for the msys2 64-bit job, due to the build could not complete within
|
||||
# the project timeout.
|
||||
- ..\msys64\usr\bin\bash -lc '../configure --target-list=x86_64-softmmu
|
||||
--without-default-devices --enable-fdt=system'
|
||||
- ..\msys64\usr\bin\bash -lc 'make'
|
||||
# qTests don't run successfully with "--without-default-devices",
|
||||
# so let's exclude the qtests from CI for now.
|
||||
- ..\msys64\usr\bin\bash -lc 'make check MTESTARGS=\"--no-suite qtest\" || { cat meson-logs/testlog.txt; exit 1; } ;'
|
||||
- .\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
|
||||
@@ -95,37 +73,27 @@ msys2-32bit:
|
||||
mingw-w64-i686-capstone
|
||||
mingw-w64-i686-curl
|
||||
mingw-w64-i686-cyrus-sasl
|
||||
mingw-w64-i686-dtc
|
||||
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-libnfs
|
||||
mingw-w64-i686-libpng
|
||||
mingw-w64-i686-libssh
|
||||
mingw-w64-i686-libtasn1
|
||||
mingw-w64-i686-libusb
|
||||
mingw-w64-i686-lzo2
|
||||
mingw-w64-i686-nettle
|
||||
mingw-w64-i686-ninja
|
||||
mingw-w64-i686-pixman
|
||||
mingw-w64-i686-pkgconf
|
||||
mingw-w64-i686-python
|
||||
mingw-w64-i686-SDL2
|
||||
mingw-w64-i686-SDL2_image
|
||||
mingw-w64-i686-snappy
|
||||
mingw-w64-i686-spice
|
||||
mingw-w64-i686-usbredir
|
||||
mingw-w64-i686-zstd "
|
||||
mingw-w64-i686-usbredir "
|
||||
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
|
||||
- $env:MSYSTEM = 'MINGW32' # Start a 32-bit MinGW environment
|
||||
- $env:MSYSTEM = 'MINGW32' # Start a 32-bit MinG environment
|
||||
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ..\msys64\usr\bin\bash -lc '../configure --target-list=ppc64-softmmu
|
||||
--enable-fdt=system'
|
||||
- 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 MTESTARGS=\"--no-suite qtest\" ||
|
||||
{ cat meson-logs/testlog.txt; exit 1; }'
|
||||
- ..\msys64\usr\bin\bash -lc 'make check || { cat meson-logs/testlog.txt; exit 1; } ;'
|
||||
|
@@ -18,11 +18,11 @@ https://www.qemu.org/contribute/security-process/
|
||||
-->
|
||||
|
||||
## Host environment
|
||||
- Operating system: <!-- Windows 10 21H1, Fedora 37, etc. -->
|
||||
- OS/kernel version: <!-- For POSIX hosts, use `uname -a` -->
|
||||
- Architecture: <!-- x86, ARM, s390x, etc. -->
|
||||
- QEMU flavor: <!-- qemu-system-x86_64, qemu-aarch64, qemu-img, etc. -->
|
||||
- QEMU version: <!-- e.g. `qemu-system-x86_64 --version` -->
|
||||
- Operating system: (Windows 10 21H1, Fedora 34, etc.)
|
||||
- OS/kernel version: (For POSIX hosts, use `uname -a`)
|
||||
- Architecture: (x86, ARM, s390x, etc.)
|
||||
- QEMU flavor: (qemu-system-x86_64, qemu-aarch64, qemu-img, etc.)
|
||||
- QEMU version: (e.g. `qemu-system-x86_64 --version`)
|
||||
- QEMU command line:
|
||||
<!--
|
||||
Give the smallest, complete command line that exhibits the problem.
|
||||
@@ -35,9 +35,9 @@ https://www.qemu.org/contribute/security-process/
|
||||
```
|
||||
|
||||
## Emulated/Virtualized environment
|
||||
- Operating system: <!-- Windows 10 21H1, Fedora 37, etc. -->
|
||||
- OS/kernel version: <!-- For POSIX guests, use `uname -a`. -->
|
||||
- Architecture: <!-- x86, ARM, s390x, etc. -->
|
||||
- Operating system: (Windows 10 21H1, Fedora 34, etc.)
|
||||
- OS/kernel version: (For POSIX guests, use `uname -a`.)
|
||||
- Architecture: (x86, ARM, s390x, etc.)
|
||||
|
||||
|
||||
## Description of problem
|
||||
|
21
.gitmodules
vendored
21
.gitmodules
vendored
@@ -13,6 +13,12 @@
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = https://gitlab.com/qemu-project/qemu-palcode.git
|
||||
[submodule "roms/sgabios"]
|
||||
path = roms/sgabios
|
||||
url = https://gitlab.com/qemu-project/sgabios.git
|
||||
[submodule "dtc"]
|
||||
path = dtc
|
||||
url = https://gitlab.com/qemu-project/dtc.git
|
||||
[submodule "roms/u-boot"]
|
||||
path = roms/u-boot
|
||||
url = https://gitlab.com/qemu-project/u-boot.git
|
||||
@@ -22,12 +28,21 @@
|
||||
[submodule "roms/QemuMacDrivers"]
|
||||
path = roms/QemuMacDrivers
|
||||
url = https://gitlab.com/qemu-project/QemuMacDrivers.git
|
||||
[submodule "ui/keycodemapdb"]
|
||||
path = ui/keycodemapdb
|
||||
url = https://gitlab.com/qemu-project/keycodemapdb.git
|
||||
[submodule "roms/seabios-hppa"]
|
||||
path = roms/seabios-hppa
|
||||
url = https://gitlab.com/qemu-project/seabios-hppa.git
|
||||
[submodule "roms/u-boot-sam460ex"]
|
||||
path = roms/u-boot-sam460ex
|
||||
url = https://gitlab.com/qemu-project/u-boot-sam460ex.git
|
||||
[submodule "tests/fp/berkeley-testfloat-3"]
|
||||
path = tests/fp/berkeley-testfloat-3
|
||||
url = https://gitlab.com/qemu-project/berkeley-testfloat-3.git
|
||||
[submodule "tests/fp/berkeley-softfloat-3"]
|
||||
path = tests/fp/berkeley-softfloat-3
|
||||
url = https://gitlab.com/qemu-project/berkeley-softfloat-3.git
|
||||
[submodule "roms/edk2"]
|
||||
path = roms/edk2
|
||||
url = https://gitlab.com/qemu-project/edk2.git
|
||||
@@ -37,9 +52,15 @@
|
||||
[submodule "roms/qboot"]
|
||||
path = roms/qboot
|
||||
url = https://gitlab.com/qemu-project/qboot.git
|
||||
[submodule "meson"]
|
||||
path = meson
|
||||
url = https://gitlab.com/qemu-project/meson.git
|
||||
[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
|
||||
|
4
.mailmap
4
.mailmap
@@ -45,7 +45,6 @@ Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nong
|
||||
Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Stefan Weil <sw@weilnetz.de> Stefan Weil via <qemu-devel@nongnu.org>
|
||||
|
||||
# Next, replace old addresses by a more recent one.
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com>
|
||||
@@ -54,10 +53,8 @@ Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
|
||||
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
|
||||
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
|
||||
Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
|
||||
Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca>
|
||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||
Christian Borntraeger <borntraeger@linux.ibm.com> <borntraeger@de.ibm.com>
|
||||
Damien Hedde <damien.hedde@dahe.fr> <damien.hedde@greensocs.com>
|
||||
Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com>
|
||||
Frederic Konrad <konrad.frederic@yahoo.fr> <fred.konrad@greensocs.com>
|
||||
Frederic Konrad <konrad.frederic@yahoo.fr> <konrad@adacore.com>
|
||||
@@ -78,7 +75,6 @@ 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>
|
||||
Taylor Simpson <ltaylorsimpson@gmail.com> <tsimpson@quicinc.com>
|
||||
|
||||
# Also list preferred name forms where people have changed their
|
||||
# git author config, or had utf8/latin1 encoding issues.
|
||||
|
64
.travis.yml
64
.travis.yml
@@ -16,6 +16,43 @@ cache:
|
||||
- $HOME/avocado/data/cache
|
||||
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# Build dependencies
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libcacard-dev
|
||||
- libgcc-7-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libsdl2-image-dev
|
||||
- libseccomp-dev
|
||||
- libspice-protocol-dev
|
||||
- libspice-server-dev
|
||||
- libssh-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
- libzstd-dev
|
||||
- ninja-build
|
||||
- sparse
|
||||
- uuid-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
|
||||
|
||||
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
|
||||
# to prevent IRC notifications from forks. This was created using:
|
||||
# $ travis encrypt -r "qemu/qemu" "irc.oftc.net#qemu"
|
||||
@@ -91,7 +128,6 @@ jobs:
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libfdt-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
@@ -113,8 +149,7 @@ jobs:
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --enable-fdt=system
|
||||
--target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false"
|
||||
- UNRELIABLE=true
|
||||
|
||||
- name: "[ppc64] GCC check-tcg"
|
||||
@@ -127,7 +162,6 @@ jobs:
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libfdt-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
@@ -149,8 +183,7 @@ jobs:
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --enable-fdt=system
|
||||
--target-list=ppc64-softmmu,ppc64le-linux-user"
|
||||
- CONFIG="--disable-containers --target-list=ppc64-softmmu,ppc64le-linux-user"
|
||||
|
||||
- name: "[s390x] GCC check-tcg"
|
||||
arch: s390x
|
||||
@@ -162,7 +195,6 @@ jobs:
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libfdt-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
@@ -184,8 +216,7 @@ jobs:
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --enable-fdt=system
|
||||
--target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
|
||||
- UNRELIABLE=true
|
||||
script:
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
@@ -206,7 +237,6 @@ jobs:
|
||||
- libattr1-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libfdt-dev
|
||||
- libgnutls28-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
@@ -225,8 +255,8 @@ jobs:
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- CONFIG="--disable-containers --enable-fdt=system --audio-drv-list=sdl
|
||||
--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CONFIG="--disable-containers --audio-drv-list=sdl --disable-user
|
||||
--target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
- name: "[s390x] GCC (user)"
|
||||
arch: s390x
|
||||
@@ -237,15 +267,13 @@ jobs:
|
||||
- libglib2.0-dev
|
||||
- libgnutls28-dev
|
||||
- ninja-build
|
||||
- flex
|
||||
- bison
|
||||
env:
|
||||
- CONFIG="--disable-containers --disable-system"
|
||||
|
||||
- name: "[s390x] Clang (disable-tcg)"
|
||||
arch: s390x
|
||||
dist: focal
|
||||
compiler: clang-10
|
||||
compiler: clang
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
@@ -253,7 +281,6 @@ jobs:
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libfdt-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
@@ -271,9 +298,8 @@ jobs:
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
- ninja-build
|
||||
- clang-10
|
||||
env:
|
||||
- TEST_CMD="make check-unit"
|
||||
- CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools
|
||||
--enable-fdt=system --host-cc=clang --cxx=clang++"
|
||||
- CONFIG="--disable-containers --disable-tcg --enable-kvm
|
||||
--disable-tools --host-cc=clang --cxx=clang++"
|
||||
- UNRELIABLE=true
|
||||
|
265
MAINTAINERS
265
MAINTAINERS
@@ -64,21 +64,6 @@ L: qemu-devel@nongnu.org
|
||||
F: *
|
||||
F: */
|
||||
|
||||
Project policy and developer guides
|
||||
R: Alex Bennée <alex.bennee@linaro.org>
|
||||
R: Daniel P. Berrangé <berrange@redhat.com>
|
||||
R: Thomas Huth <thuth@redhat.com>
|
||||
R: Markus Armbruster <armbru@redhat.com>
|
||||
R: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Juan Quintela <quintela@redhat.com>
|
||||
W: https://www.qemu.org/docs/master/devel/index.html
|
||||
S: Odd Fixes
|
||||
F: docs/devel/style.rst
|
||||
F: docs/devel/code-of-conduct.rst
|
||||
F: docs/devel/conflict-resolution.rst
|
||||
F: docs/devel/submitting-a-patch.rst
|
||||
F: docs/devel/submitting-a-pull-request.rst
|
||||
|
||||
Responsible Disclosure, Reporting Security Issues
|
||||
-------------------------------------------------
|
||||
W: https://wiki.qemu.org/SecurityProcess
|
||||
@@ -93,7 +78,6 @@ M: Laurent Vivier <laurent@vivier.eu>
|
||||
S: Maintained
|
||||
L: qemu-trivial@nongnu.org
|
||||
K: ^Subject:.*(?i)trivial
|
||||
F: docs/devel/trivial-patches.rst
|
||||
T: git git://git.corpit.ru/qemu.git trivial-patches
|
||||
T: git https://github.com/vivier/qemu.git trivial-patches
|
||||
|
||||
@@ -128,8 +112,6 @@ M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
|
||||
S: Odd Fixes
|
||||
K: ^Subject:.*(?i)mips
|
||||
F: docs/system/target-mips.rst
|
||||
F: configs/targets/mips*
|
||||
|
||||
Guest CPU cores (TCG)
|
||||
---------------------
|
||||
@@ -138,7 +120,6 @@ M: Richard Henderson <richard.henderson@linaro.org>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: softmmu/cpus.c
|
||||
F: softmmu/watchpoint.c
|
||||
F: cpus-common.c
|
||||
F: page-vary.c
|
||||
F: page-vary-common.c
|
||||
@@ -148,20 +129,12 @@ F: util/cacheinfo.c
|
||||
F: util/cacheflush.c
|
||||
F: scripts/decodetree.py
|
||||
F: docs/devel/decodetree.rst
|
||||
F: docs/devel/tcg*
|
||||
F: include/exec/cpu*.h
|
||||
F: include/exec/exec-all.h
|
||||
F: include/exec/tb-flush.h
|
||||
F: include/exec/target_long.h
|
||||
F: include/exec/helper*.h
|
||||
F: include/exec/helper*.h.inc
|
||||
F: include/exec/helper-info.c.inc
|
||||
F: include/sysemu/cpus.h
|
||||
F: include/sysemu/tcg.h
|
||||
F: include/hw/core/tcg-cpu-ops.h
|
||||
F: host/include/*/host/cpuinfo.h
|
||||
F: util/cpuinfo-*.c
|
||||
F: include/tcg/
|
||||
|
||||
FPU emulation
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
@@ -184,7 +157,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: target/arm/
|
||||
F: target/arm/tcg/
|
||||
F: tests/tcg/arm/
|
||||
F: tests/tcg/aarch64/
|
||||
F: tests/qtest/arm-cpu-features.c
|
||||
@@ -220,24 +192,15 @@ F: tests/tcg/cris/
|
||||
F: disas/cris.c
|
||||
|
||||
Hexagon TCG CPUs
|
||||
M: Brian Cain <bcain@quicinc.com>
|
||||
M: Taylor Simpson <tsimpson@quicinc.com>
|
||||
S: Supported
|
||||
F: target/hexagon/
|
||||
X: target/hexagon/idef-parser/
|
||||
X: target/hexagon/gen_idef_parser_funcs.py
|
||||
F: linux-user/hexagon/
|
||||
F: tests/tcg/hexagon/
|
||||
F: disas/hexagon.c
|
||||
F: configs/targets/hexagon-linux-user/default.mak
|
||||
F: docker/dockerfiles/debian-hexagon-cross.docker
|
||||
F: gdb-xml/hexagon*.xml
|
||||
|
||||
Hexagon idef-parser
|
||||
M: Alessandro Di Federico <ale@rev.ng>
|
||||
M: Anton Johansson <anjo@rev.ng>
|
||||
S: Supported
|
||||
F: target/hexagon/idef-parser/
|
||||
F: target/hexagon/gen_idef_parser_funcs.py
|
||||
F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh
|
||||
|
||||
HPPA (PA-RISC) TCG CPUs
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
@@ -251,7 +214,6 @@ M: Xiaojuan Yang <yangxiaojuan@loongson.cn>
|
||||
S: Maintained
|
||||
F: target/loongarch/
|
||||
F: tests/tcg/loongarch64/
|
||||
F: tests/avocado/machine_loongarch.py
|
||||
|
||||
M68K TCG CPUs
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
@@ -266,6 +228,7 @@ F: target/microblaze/
|
||||
F: hw/microblaze/
|
||||
F: disas/microblaze.c
|
||||
F: tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh
|
||||
F: tests/tcg/nios2/Makefile.target
|
||||
|
||||
MIPS TCG CPUs
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
@@ -279,20 +242,18 @@ F: docs/system/cpu-models-mips.rst.inc
|
||||
F: tests/tcg/mips/
|
||||
|
||||
NiosII TCG CPUs
|
||||
R: Chris Wulff <crwulff@gmail.com>
|
||||
R: Marek Vasut <marex@denx.de>
|
||||
S: Orphan
|
||||
M: Chris Wulff <crwulff@gmail.com>
|
||||
M: Marek Vasut <marex@denx.de>
|
||||
S: Maintained
|
||||
F: target/nios2/
|
||||
F: hw/nios2/
|
||||
F: disas/nios2.c
|
||||
F: configs/devices/nios2-softmmu/default.mak
|
||||
F: tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh
|
||||
F: tests/tcg/nios2/
|
||||
|
||||
OpenRISC TCG CPUs
|
||||
M: Stafford Horne <shorne@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: docs/system/openrisc/cpu-features.rst
|
||||
F: target/openrisc/
|
||||
F: hw/openrisc/
|
||||
F: tests/tcg/openrisc/
|
||||
@@ -302,9 +263,8 @@ M: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Cédric Le Goater <clg@kaod.org>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
R: Greg Kurz <groug@kaod.org>
|
||||
R: Nicholas Piggin <npiggin@gmail.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: target/ppc/
|
||||
F: hw/ppc/ppc.c
|
||||
F: hw/ppc/ppc_booke.c
|
||||
@@ -314,9 +274,6 @@ RISC-V TCG CPUs
|
||||
M: Palmer Dabbelt <palmer@dabbelt.com>
|
||||
M: Alistair Francis <alistair.francis@wdc.com>
|
||||
M: Bin Meng <bin.meng@windriver.com>
|
||||
R: Weiwei Li <liweiwei@iscas.ac.cn>
|
||||
R: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
|
||||
R: Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: target/riscv/
|
||||
@@ -325,18 +282,10 @@ F: include/hw/riscv/
|
||||
F: linux-user/host/riscv32/
|
||||
F: linux-user/host/riscv64/
|
||||
|
||||
RISC-V XThead* extensions
|
||||
M: Christoph Muellner <christoph.muellner@vrull.eu>
|
||||
M: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: target/riscv/insn_trans/trans_xthead.c.inc
|
||||
F: target/riscv/xthead*.decode
|
||||
|
||||
RISC-V XVentanaCondOps extension
|
||||
M: Philipp Tomsich <philipp.tomsich@vrull.eu>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: target/riscv/XVentanaCondOps.decode
|
||||
F: target/riscv/insn_trans/trans_xventanacondops.c.inc
|
||||
|
||||
@@ -383,7 +332,6 @@ F: target/i386/tcg/
|
||||
F: tests/tcg/i386/
|
||||
F: tests/tcg/x86_64/
|
||||
F: hw/i386/
|
||||
F: docs/system/i386/cpu.rst
|
||||
F: docs/system/cpu-models-x86*
|
||||
T: git https://gitlab.com/ehabkost/qemu.git x86-next
|
||||
|
||||
@@ -394,7 +342,6 @@ S: Maintained
|
||||
F: target/xtensa/
|
||||
F: hw/xtensa/
|
||||
F: tests/tcg/xtensa/
|
||||
F: tests/tcg/xtensaeb/
|
||||
F: disas/xtensa.c
|
||||
F: include/hw/xtensa/xtensa-isa.h
|
||||
F: configs/devices/xtensa*/default.mak
|
||||
@@ -442,7 +389,7 @@ M: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Cédric Le Goater <clg@kaod.org>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
R: Greg Kurz <groug@kaod.org>
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: target/ppc/kvm.c
|
||||
|
||||
S390 KVM CPUs
|
||||
@@ -469,15 +416,6 @@ F: target/i386/kvm/
|
||||
F: target/i386/sev*
|
||||
F: scripts/kvm/vmxcap
|
||||
|
||||
Xen emulation on X86 KVM CPUs
|
||||
M: David Woodhouse <dwmw2@infradead.org>
|
||||
M: Paul Durrant <paul@xen.org>
|
||||
S: Supported
|
||||
F: include/sysemu/kvm_xen.h
|
||||
F: target/i386/kvm/xen*
|
||||
F: hw/i386/kvm/xen*
|
||||
F: tests/avocado/xen_guest.py
|
||||
|
||||
Guest CPU Cores (other accelerators)
|
||||
------------------------------------
|
||||
Overall
|
||||
@@ -485,7 +423,7 @@ M: Richard Henderson <richard.henderson@linaro.org>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: include/qemu/accel.h
|
||||
F: include/sysemu/accel-*.h
|
||||
F: include/sysemu/accel-ops.h
|
||||
F: include/hw/core/accel-cpu.h
|
||||
F: accel/accel-*.c
|
||||
F: accel/Makefile.objs
|
||||
@@ -548,7 +486,10 @@ F: stubs/xen-hw-stub.c
|
||||
Guest CPU Cores (HAXM)
|
||||
---------------------
|
||||
X86 HAXM CPUs
|
||||
S: Orphan
|
||||
M: Wenchao Wang <wenchao.wang@intel.com>
|
||||
L: haxm-team@intel.com
|
||||
W: https://github.com/intel/haxm/issues
|
||||
S: Maintained
|
||||
F: accel/stubs/hax-stub.c
|
||||
F: include/sysemu/hax.h
|
||||
F: target/i386/hax/
|
||||
@@ -556,6 +497,7 @@ F: target/i386/hax/
|
||||
Guest CPU Cores (NVMM)
|
||||
----------------------
|
||||
NetBSD Virtual Machine Monitor (NVMM) CPU support
|
||||
M: Kamil Rytarowski <kamil@netbsd.org>
|
||||
M: Reinoud Zandijk <reinoud@netbsd.org>
|
||||
S: Maintained
|
||||
F: include/sysemu/nvmm.h
|
||||
@@ -580,6 +522,7 @@ F: util/*posix*.c
|
||||
F: include/qemu/*posix*.h
|
||||
|
||||
NETBSD
|
||||
M: Kamil Rytarowski <kamil@netbsd.org>
|
||||
M: Reinoud Zandijk <reinoud@netbsd.org>
|
||||
M: Ryo ONODERA <ryoon@netbsd.org>
|
||||
S: Maintained
|
||||
@@ -620,14 +563,12 @@ ARM Machines
|
||||
Allwinner-a10
|
||||
M: Beniamino Galvani <b.galvani@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/*/allwinner*
|
||||
F: include/hw/*/allwinner*
|
||||
F: hw/arm/cubieboard.c
|
||||
F: docs/system/arm/cubieboard.rst
|
||||
F: hw/misc/axp209.c
|
||||
|
||||
Allwinner-h3
|
||||
M: Niek Linnenbank <nieklinnenbank@gmail.com>
|
||||
@@ -847,13 +788,13 @@ F: include/hw/net/mv88w8618_eth.h
|
||||
F: docs/system/arm/musicpal.rst
|
||||
|
||||
Nuvoton NPCM7xx
|
||||
M: Havard Skinnemoen <hskinnemoen@google.com>
|
||||
M: Tyrone Ting <kfting@nuvoton.com>
|
||||
M: Hao Wu <wuhaotsh@google.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Supported
|
||||
F: hw/*/npcm*
|
||||
F: include/hw/*/npcm*
|
||||
F: tests/qtest/npcm*
|
||||
F: hw/*/npcm7xx*
|
||||
F: include/hw/*/npcm7xx*
|
||||
F: tests/qtest/npcm7xx*
|
||||
F: pc-bios/npcm7xx_bootrom.bin
|
||||
F: roms/vbootrom
|
||||
F: docs/system/arm/nuvoton.rst
|
||||
@@ -932,7 +873,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Jean-Christophe Dubois <jcd@tribudubois.net>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: docs/system/arm/sabrelite.rst
|
||||
F: hw/arm/sabrelite.c
|
||||
F: hw/arm/fsl-imx6.c
|
||||
F: hw/misc/imx6_*.c
|
||||
@@ -947,12 +887,10 @@ SBSA-REF
|
||||
M: Radoslaw Biernacki <rad@semihalf.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Leif Lindholm <quic_llindhol@quicinc.com>
|
||||
R: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/sbsa-ref.c
|
||||
F: docs/system/arm/sbsa.rst
|
||||
F: tests/avocado/machine_aarch64_sbsaref.py
|
||||
|
||||
Sharp SL-5500 (Collie) PDA
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -989,7 +927,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/versatile*
|
||||
F: hw/i2c/arm_sbcon_i2c.c
|
||||
F: include/hw/i2c/arm_sbcon_i2c.h
|
||||
F: hw/misc/arm_sysctl.c
|
||||
F: docs/system/arm/versatile.rst
|
||||
@@ -1036,6 +973,12 @@ S: Maintained
|
||||
F: hw/ssi/xlnx-versal-ospi.c
|
||||
F: include/hw/ssi/xlnx-versal-ospi.h
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <shannon.zhaosl@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
|
||||
STM32F100
|
||||
M: Alexandre Iooss <erdnaxe@crans.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
@@ -1078,12 +1021,6 @@ L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/netduinoplus2.c
|
||||
|
||||
Olimex STM32 H405
|
||||
M: Felipe Balbi <balbi@kernel.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/olimex-stm32-h405.c
|
||||
|
||||
SmartFusion2
|
||||
M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -1122,7 +1059,7 @@ F: include/hw/misc/pca9552*.h
|
||||
F: hw/net/ftgmac100.c
|
||||
F: include/hw/net/ftgmac100.h
|
||||
F: docs/system/arm/aspeed.rst
|
||||
F: tests/*/*aspeed*
|
||||
F: tests/qtest/*aspeed*
|
||||
F: hw/arm/fby35.c
|
||||
|
||||
NRF51
|
||||
@@ -1226,7 +1163,6 @@ q800
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
S: Maintained
|
||||
F: hw/m68k/q800.c
|
||||
F: hw/m68k/q800-glue.c
|
||||
F: hw/misc/mac_via.c
|
||||
F: hw/nubus/*
|
||||
F: hw/display/macfb.c
|
||||
@@ -1238,8 +1174,6 @@ F: include/hw/misc/mac_via.h
|
||||
F: include/hw/nubus/*
|
||||
F: include/hw/display/macfb.h
|
||||
F: include/hw/block/swim.h
|
||||
F: include/hw/m68k/q800.h
|
||||
F: include/hw/m68k/q800-glue.h
|
||||
|
||||
virt
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
@@ -1292,7 +1226,7 @@ S: Odd Fixes
|
||||
F: hw/isa/piix4.c
|
||||
F: hw/acpi/piix4.c
|
||||
F: hw/mips/malta.c
|
||||
F: hw/pci-host/gt64120.c
|
||||
F: hw/mips/gt64xxx_pci.c
|
||||
F: include/hw/southbridge/piix.h
|
||||
F: tests/avocado/linux_ssh_mips_malta.py
|
||||
F: tests/avocado/machine_mips_malta.py
|
||||
@@ -1313,7 +1247,6 @@ F: hw/isa/vt82c686.c
|
||||
F: hw/pci-host/bonito.c
|
||||
F: hw/usb/vt82c686-uhci-pci.c
|
||||
F: include/hw/isa/vt82c686.h
|
||||
F: include/hw/pci-host/bonito.h
|
||||
F: tests/avocado/machine_mips_fuloong2e.py
|
||||
|
||||
Loongson-3 virtual platforms
|
||||
@@ -1340,7 +1273,6 @@ OpenRISC Machines
|
||||
or1k-sim
|
||||
M: Jia Liu <proljc@gmail.com>
|
||||
S: Maintained
|
||||
F: docs/system/openrisc/or1k-sim.rst
|
||||
F: hw/openrisc/openrisc_sim.c
|
||||
|
||||
PowerPC Machines
|
||||
@@ -1434,9 +1366,8 @@ M: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Cédric Le Goater <clg@kaod.org>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
R: Greg Kurz <groug@kaod.org>
|
||||
R: Harsh Prateek Bora <harshpb@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
S: Maintained
|
||||
F: hw/*/spapr*
|
||||
F: include/hw/*/spapr*
|
||||
F: hw/*/xics*
|
||||
@@ -1452,8 +1383,6 @@ F: tests/avocado/ppc_pseries.py
|
||||
|
||||
PowerNV (Non-Virtualized)
|
||||
M: Cédric Le Goater <clg@kaod.org>
|
||||
R: Frédéric Barrat <fbarrat@linux.ibm.com>
|
||||
R: Nicholas Piggin <npiggin@gmail.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: docs/system/ppc/powernv.rst
|
||||
@@ -1715,10 +1644,10 @@ F: hw/isa/piix3.c
|
||||
F: hw/isa/lpc_ich9.c
|
||||
F: hw/i2c/smbus_ich9.c
|
||||
F: hw/acpi/piix4.c
|
||||
F: hw/acpi/ich9*.c
|
||||
F: include/hw/acpi/ich9*.h
|
||||
F: include/hw/southbridge/ich9.h
|
||||
F: hw/acpi/ich9.c
|
||||
F: include/hw/acpi/ich9.h
|
||||
F: include/hw/southbridge/piix.h
|
||||
F: hw/misc/sga.c
|
||||
F: hw/isa/apm.c
|
||||
F: include/hw/isa/apm.h
|
||||
F: tests/unit/test-x86-cpuid.c
|
||||
@@ -1746,11 +1675,10 @@ F: hw/rtc/mc146818rtc*
|
||||
F: hw/watchdog/wdt_ib700.c
|
||||
F: hw/watchdog/wdt_i6300esb.c
|
||||
F: include/hw/display/vga.h
|
||||
F: include/hw/char/parallel*.h
|
||||
F: include/hw/char/parallel.h
|
||||
F: include/hw/dma/i8257.h
|
||||
F: include/hw/i2c/pm_smbus.h
|
||||
F: include/hw/input/i8042.h
|
||||
F: include/hw/intc/ioapic*
|
||||
F: include/hw/isa/i8259_internal.h
|
||||
F: include/hw/isa/superio.h
|
||||
F: include/hw/timer/hpet.h
|
||||
@@ -1825,7 +1753,7 @@ M: Francisco Iglesias <francisco.iglesias@amd.com>
|
||||
S: Maintained
|
||||
F: hw/net/can/xlnx-*
|
||||
F: include/hw/net/xlnx-*
|
||||
F: tests/qtest/xlnx-can*-test*
|
||||
F: tests/qtest/xlnx-can-test*
|
||||
|
||||
EDU
|
||||
M: Jiri Slaby <jslaby@suse.cz>
|
||||
@@ -1835,7 +1763,7 @@ F: hw/misc/edu.c
|
||||
IDE
|
||||
M: John Snow <jsnow@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Odd Fixes
|
||||
S: Supported
|
||||
F: include/hw/ide.h
|
||||
F: include/hw/ide/
|
||||
F: hw/ide/
|
||||
@@ -1860,7 +1788,7 @@ T: git https://github.com/cminyard/qemu.git master-ipmi-rebase
|
||||
Floppy
|
||||
M: John Snow <jsnow@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Odd Fixes
|
||||
S: Supported
|
||||
F: hw/block/fdc.c
|
||||
F: hw/block/fdc-internal.h
|
||||
F: hw/block/fdc-isa.c
|
||||
@@ -1911,7 +1839,7 @@ F: hw/pci/pcie_doe.c
|
||||
ACPI/SMBIOS
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Igor Mammedov <imammedo@redhat.com>
|
||||
R: Ani Sinha <anisinha@redhat.com>
|
||||
R: Ani Sinha <ani@anisinha.ca>
|
||||
S: Supported
|
||||
F: include/hw/acpi/*
|
||||
F: include/hw/firmware/smbios.h
|
||||
@@ -1929,18 +1857,6 @@ F: docs/specs/acpi_nvdimm.rst
|
||||
F: docs/specs/acpi_pci_hotplug.rst
|
||||
F: docs/specs/acpi_hw_reduced_hotplug.rst
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <shannon.zhaosl@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
|
||||
RISC-V ACPI Subsystem
|
||||
M: Sunil V L <sunilvl@ventanamicro.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/riscv/virt-acpi-build.c
|
||||
|
||||
ACPI/VIOT
|
||||
M: Jean-Philippe Brucker <jean-philippe@linaro.org>
|
||||
S: Supported
|
||||
@@ -1948,7 +1864,7 @@ F: hw/acpi/viot.c
|
||||
F: hw/acpi/viot.h
|
||||
|
||||
ACPI/AVOCADO/BIOSBITS
|
||||
M: Ani Sinha <anisinha@redhat.com>
|
||||
M: Ani Sinha <ani@anisinha.ca>
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: tests/avocado/acpi-bits/*
|
||||
@@ -2051,7 +1967,6 @@ F: hw/usb/dev-serial.c
|
||||
|
||||
VFIO
|
||||
M: Alex Williamson <alex.williamson@redhat.com>
|
||||
R: Cédric Le Goater <clg@redhat.com>
|
||||
S: Supported
|
||||
F: hw/vfio/*
|
||||
F: include/hw/vfio/
|
||||
@@ -2092,10 +2007,6 @@ F: backends/vhost-user.c
|
||||
F: include/sysemu/vhost-user-backend.h
|
||||
F: subprojects/libvhost-user/
|
||||
|
||||
vhost-shadow-virtqueue
|
||||
R: Eugenio Pérez <eperezma@redhat.com>
|
||||
F: hw/virtio/vhost-shadow-virtqueue.*
|
||||
|
||||
virtio
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
@@ -2105,7 +2016,6 @@ F: hw/virtio/trace-events
|
||||
F: qapi/virtio.json
|
||||
F: net/vhost-user.c
|
||||
F: include/hw/virtio/
|
||||
F: docs/devel/virtio*
|
||||
|
||||
virtio-balloon
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
@@ -2127,7 +2037,6 @@ X: hw/9pfs/xen-9p*
|
||||
F: fsdev/
|
||||
F: docs/tools/virtfs-proxy-helper.rst
|
||||
F: tests/qtest/virtio-9p-test.c
|
||||
F: tests/qtest/libqos/virtio-9p*
|
||||
T: git https://gitlab.com/gkurz/qemu.git 9p-next
|
||||
T: git https://github.com/cschoenebeck/qemu.git 9p.next
|
||||
|
||||
@@ -2154,10 +2063,13 @@ T: git https://github.com/borntraeger/qemu.git s390-next
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
virtiofs
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Supported
|
||||
F: tools/virtiofsd/*
|
||||
F: hw/virtio/vhost-user-fs*
|
||||
F: include/hw/virtio/vhost-user-fs.h
|
||||
F: docs/tools/virtiofsd.rst
|
||||
L: virtio-fs@redhat.com
|
||||
|
||||
virtio-input
|
||||
@@ -2196,7 +2108,7 @@ F: tests/qtest/virtio-rng-test.c
|
||||
vhost-user-rng
|
||||
M: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
S: Supported
|
||||
F: docs/system/devices/vhost-user-rng.rst
|
||||
F: docs/tools/vhost-user-rng.rst
|
||||
F: hw/virtio/vhost-user-rng.c
|
||||
F: hw/virtio/vhost-user-rng-pci.c
|
||||
F: include/hw/virtio/vhost-user-rng.h
|
||||
@@ -2234,7 +2146,7 @@ S: Supported
|
||||
F: hw/nvme/*
|
||||
F: include/block/nvme.h
|
||||
F: tests/qtest/nvme-test.c
|
||||
F: docs/system/devices/nvme.rst
|
||||
F: docs/system/nvme.rst
|
||||
T: git git://git.infradead.org/qemu-nvme.git nvme-next
|
||||
|
||||
megasas
|
||||
@@ -2248,7 +2160,6 @@ F: tests/qtest/fuzz-megasas-test.c
|
||||
|
||||
Network packet abstractions
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
S: Maintained
|
||||
F: include/net/eth.h
|
||||
F: net/eth.c
|
||||
@@ -2272,28 +2183,14 @@ F: docs/specs/rocker.txt
|
||||
|
||||
e1000x
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
S: Maintained
|
||||
F: hw/net/e1000x*
|
||||
|
||||
e1000e
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
S: Maintained
|
||||
F: hw/net/e1000e*
|
||||
F: tests/qtest/fuzz-e1000e-test.c
|
||||
F: tests/qtest/e1000e-test.c
|
||||
F: tests/qtest/libqos/e1000e.*
|
||||
|
||||
igb
|
||||
M: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Sriram Yagnaraman <sriram.yagnaraman@est.tech>
|
||||
S: Maintained
|
||||
F: docs/system/devices/igb.rst
|
||||
F: hw/net/igb*
|
||||
F: tests/avocado/netdev-ethtool.py
|
||||
F: tests/qtest/igb-test.c
|
||||
F: tests/qtest/libqos/igb.c
|
||||
|
||||
eepro100
|
||||
M: Stefan Weil <sw@weilnetz.de>
|
||||
@@ -2347,6 +2244,7 @@ F: hw/acpi/vmgenid.c
|
||||
F: include/hw/acpi/vmgenid.h
|
||||
F: docs/specs/vmgenid.txt
|
||||
F: tests/qtest/vmgenid-test.c
|
||||
F: stubs/vmgenid.c
|
||||
|
||||
LED
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
@@ -2448,7 +2346,6 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next
|
||||
|
||||
XIVE
|
||||
M: Cédric Le Goater <clg@kaod.org>
|
||||
R: Frédéric Barrat <fbarrat@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/*/*xive*
|
||||
@@ -2558,7 +2455,6 @@ Subsystems
|
||||
----------
|
||||
Overall Audio backends
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/
|
||||
X: audio/alsaaudio.c
|
||||
@@ -2582,7 +2478,7 @@ Core Audio framework backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Christian Schoenebeck <qemu_oss@crudebyte.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: audio/coreaudio.c
|
||||
|
||||
@@ -2659,7 +2555,6 @@ F: util/aio-*.c
|
||||
F: util/aio-*.h
|
||||
F: util/fdmon-*.c
|
||||
F: block/io.c
|
||||
F: block/plug.c
|
||||
F: migration/block*
|
||||
F: include/block/aio.h
|
||||
F: include/block/aio-wait.h
|
||||
@@ -2705,8 +2600,8 @@ T: git https://gitlab.com/jsnow/qemu.git jobs
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
Compute Express Link
|
||||
M: Ben Widawsky <ben.widawsky@intel.com>
|
||||
M: Jonathan Cameron <jonathan.cameron@huawei.com>
|
||||
R: Fan Ni <fan.ni@samsung.com>
|
||||
S: Supported
|
||||
F: hw/cxl/
|
||||
F: hw/mem/cxl_type3.c
|
||||
@@ -2801,14 +2696,11 @@ GDB stub
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
R: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
S: Maintained
|
||||
F: docs/system/gdb.rst
|
||||
F: gdbstub/*
|
||||
F: include/exec/gdbstub.h
|
||||
F: include/gdbstub/*
|
||||
F: gdb-xml/
|
||||
F: tests/tcg/multiarch/gdbstub/
|
||||
F: scripts/feature_to_c.sh
|
||||
F: scripts/probe-gdb-support.py
|
||||
|
||||
Memory API
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
@@ -2856,24 +2748,23 @@ F: docs/spice-port-fqdn.txt
|
||||
|
||||
Graphics
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: ui/
|
||||
F: include/ui/
|
||||
F: qapi/ui.json
|
||||
F: util/drm.c
|
||||
F: docs/devel/ui.rst
|
||||
|
||||
Cocoa graphics
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: ui/cocoa.m
|
||||
|
||||
Main loop
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: include/exec/gen-icount.h
|
||||
F: include/qemu/main-loop.h
|
||||
F: include/sysemu/runstate.h
|
||||
F: include/sysemu/runstate-action.h
|
||||
@@ -2885,21 +2776,20 @@ F: softmmu/cpus.c
|
||||
F: softmmu/cpu-throttle.c
|
||||
F: softmmu/cpu-timers.c
|
||||
F: softmmu/icount.c
|
||||
F: softmmu/runstate*
|
||||
F: softmmu/runstate-action.c
|
||||
F: softmmu/runstate.c
|
||||
F: qapi/run-state.json
|
||||
|
||||
Read, Copy, Update (RCU)
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: docs/devel/lockcnt.txt
|
||||
F: docs/devel/rcu.txt
|
||||
F: include/qemu/rcu*.h
|
||||
F: tests/unit/rcutorture.c
|
||||
F: tests/unit/test-rcu-*.c
|
||||
F: util/rcu.c
|
||||
|
||||
Human Monitor (HMP)
|
||||
M: Dr. David Alan Gilbert <dave@treblig.org>
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
S: Maintained
|
||||
F: monitor/monitor-internal.h
|
||||
F: monitor/misc.c
|
||||
@@ -2939,11 +2829,9 @@ T: git https://gitlab.com/ehabkost/qemu.git machine-next
|
||||
|
||||
Cryptodev Backends
|
||||
M: Gonglei <arei.gonglei@huawei.com>
|
||||
M: zhenwei pi <pizhenwei@bytedance.com>
|
||||
S: Maintained
|
||||
F: include/sysemu/cryptodev*.h
|
||||
F: backends/cryptodev*.c
|
||||
F: qapi/cryptodev.json
|
||||
|
||||
Python library
|
||||
M: John Snow <jsnow@redhat.com>
|
||||
@@ -3042,7 +2930,6 @@ M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Daniel P. Berrange <berrange@redhat.com>
|
||||
R: Eduardo Habkost <eduardo@habkost.net>
|
||||
S: Supported
|
||||
F: docs/devel/qom.rst
|
||||
F: docs/qdev-device-use.txt
|
||||
F: hw/core/qdev*
|
||||
F: hw/core/bus.c
|
||||
@@ -3053,7 +2940,6 @@ F: include/qom/
|
||||
F: qapi/qom.json
|
||||
F: qapi/qdev.json
|
||||
F: scripts/coccinelle/qom-parent-type.cocci
|
||||
F: scripts/qom-cast-macro-clean-cocci-gen.py
|
||||
F: softmmu/qdev-monitor.c
|
||||
F: stubs/qdev.c
|
||||
F: qom/
|
||||
@@ -3092,7 +2978,6 @@ F: softmmu/qtest.c
|
||||
F: accel/qtest/
|
||||
F: tests/qtest/
|
||||
F: docs/devel/qgraph.rst
|
||||
F: docs/devel/qtest.rst
|
||||
X: tests/qtest/bios-tables-test*
|
||||
|
||||
Device Fuzzing
|
||||
@@ -3124,11 +3009,6 @@ F: net/slirp.c
|
||||
F: include/net/slirp.h
|
||||
T: git https://people.debian.org/~sthibault/qemu.git slirp
|
||||
|
||||
Stats
|
||||
S: Orphan
|
||||
F: include/sysemu/stats.h
|
||||
F: stats/
|
||||
|
||||
Streams
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
@@ -3157,14 +3037,13 @@ T: git https://github.com/stefanha/qemu.git tracing
|
||||
TPM
|
||||
M: Stefan Berger <stefanb@linux.ibm.com>
|
||||
S: Maintained
|
||||
F: softmmu/tpm*
|
||||
F: tpm.c
|
||||
F: hw/tpm/*
|
||||
F: include/hw/acpi/tpm.h
|
||||
F: include/sysemu/tpm*
|
||||
F: qapi/tpm.json
|
||||
F: backends/tpm/
|
||||
F: tests/qtest/*tpm*
|
||||
F: docs/specs/tpm.rst
|
||||
T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
|
||||
|
||||
Checkpatch
|
||||
@@ -3173,13 +3052,11 @@ F: scripts/checkpatch.pl
|
||||
|
||||
Migration
|
||||
M: Juan Quintela <quintela@redhat.com>
|
||||
R: Peter Xu <peterx@redhat.com>
|
||||
R: Leonardo Bras <leobras@redhat.com>
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/core/vmstate-if.c
|
||||
F: include/hw/vmstate-if.h
|
||||
F: include/migration/
|
||||
F: include/qemu/userfaultfd.h
|
||||
F: migration/
|
||||
F: scripts/vmstate-static-checker.py
|
||||
F: tests/vmstate-static-checker-data/
|
||||
@@ -3187,7 +3064,6 @@ F: tests/qtest/migration-test.c
|
||||
F: docs/devel/migration.rst
|
||||
F: qapi/migration.json
|
||||
F: tests/migration/
|
||||
F: util/userfaultfd.c
|
||||
|
||||
D-Bus
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
@@ -3318,10 +3194,8 @@ S: Supported
|
||||
F: replay/*
|
||||
F: block/blkreplay.c
|
||||
F: net/filter-replay.c
|
||||
F: include/exec/replay-core.h
|
||||
F: include/sysemu/replay.h
|
||||
F: docs/devel/replay.rst
|
||||
F: docs/system/replay.rst
|
||||
F: docs/replay.txt
|
||||
F: stubs/replay.c
|
||||
F: tests/avocado/replay_kernel.py
|
||||
F: tests/avocado/replay_linux.py
|
||||
@@ -3383,6 +3257,8 @@ F: roms/edk2
|
||||
F: roms/edk2-*
|
||||
F: tests/data/uefi-boot-images/
|
||||
F: tests/uefi-test-tools/
|
||||
F: .gitlab-ci.d/edk2.yml
|
||||
F: .gitlab-ci.d/edk2/
|
||||
|
||||
VT-d Emulation
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
@@ -3393,10 +3269,6 @@ F: hw/i386/intel_iommu.c
|
||||
F: hw/i386/intel_iommu_internal.h
|
||||
F: include/hw/i386/intel_iommu.h
|
||||
|
||||
AMD-Vi Emulation
|
||||
S: Orphan
|
||||
F: hw/i386/amd_iommu.?
|
||||
|
||||
OpenSBI Firmware
|
||||
M: Bin Meng <bmeng.cn@gmail.com>
|
||||
S: Supported
|
||||
@@ -3406,7 +3278,7 @@ F: .gitlab-ci.d/opensbi/
|
||||
|
||||
Clock framework
|
||||
M: Luc Michel <luc@lmichel.fr>
|
||||
R: Damien Hedde <damien.hedde@dahe.fr>
|
||||
R: Damien Hedde <damien.hedde@greensocs.com>
|
||||
S: Maintained
|
||||
F: include/hw/clock.h
|
||||
F: include/hw/qdev-clock.h
|
||||
@@ -3665,11 +3537,13 @@ F: block/dmg.c
|
||||
parallels
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Denis V. Lunev <den@openvz.org>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/parallels.c
|
||||
F: block/parallels-ext.c
|
||||
F: docs/interop/parallels.txt
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
qed
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
@@ -3793,6 +3667,7 @@ F: tests/tcg/aarch64/system/semiheap.c
|
||||
Multi-process QEMU
|
||||
M: Elena Ufimtseva <elena.ufimtseva@oracle.com>
|
||||
M: Jagannathan Raman <jag.raman@oracle.com>
|
||||
M: John G Johnson <john.g.johnson@oracle.com>
|
||||
S: Maintained
|
||||
F: docs/devel/multi-process.rst
|
||||
F: docs/system/multi-process.rst
|
||||
@@ -3843,9 +3718,7 @@ F: scripts/ci/
|
||||
F: tests/docker/
|
||||
F: tests/vm/
|
||||
F: tests/lcitool/
|
||||
F: tests/avocado/tuxrun_baselines.py
|
||||
F: scripts/archive-source.sh
|
||||
F: docs/devel/testing.rst
|
||||
W: https://gitlab.com/qemu-project/qemu/pipelines
|
||||
W: https://travis-ci.org/qemu/qemu
|
||||
|
||||
@@ -3860,7 +3733,8 @@ W: https://cirrus-ci.com/github/qemu/qemu
|
||||
Windows Hosted Continuous Integration
|
||||
M: Yonggang Luo <luoyonggang@gmail.com>
|
||||
S: Maintained
|
||||
F: .gitlab-ci.d/windows.yml
|
||||
F: .cirrus.yml
|
||||
W: https://cirrus-ci.com/github/qemu/qemu
|
||||
|
||||
Guest Test Compilation Support
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
@@ -3926,16 +3800,6 @@ F: configure
|
||||
F: scripts/mtest2make.py
|
||||
F: tests/Makefile.include
|
||||
|
||||
Kconfig
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: scripts/minikconf.py
|
||||
F: docs/devel/kconfig.rst
|
||||
F: Kconfig*
|
||||
F: */Kconfig*
|
||||
F: hw/*/Kconfig*
|
||||
F: target/*/Kconfig*
|
||||
|
||||
GIT submodules
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Odd Fixes
|
||||
@@ -3959,8 +3823,3 @@ Performance Tools and Tests
|
||||
M: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
|
||||
S: Maintained
|
||||
F: scripts/performance/
|
||||
|
||||
Code Coverage Tools
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
S: Odd Fixes
|
||||
F: scripts/coverage/
|
||||
|
45
Makefile
45
Makefile
@@ -26,7 +26,7 @@ quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1
|
||||
quiet-@ = $(if $(V),,@)
|
||||
quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3)
|
||||
|
||||
UNCHECKED_GOALS := TAGS gtags cscope ctags dist \
|
||||
UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
|
||||
help check-help print-% \
|
||||
docker docker-% vm-help vm-test vm-build-%
|
||||
|
||||
@@ -45,6 +45,18 @@ 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
|
||||
|
||||
.PHONY: git-submodule-update
|
||||
git-submodule-update:
|
||||
ifneq ($(GIT_SUBMODULES_ACTION),ignore)
|
||||
$(call quiet-command, \
|
||||
(GIT="$(GIT)" "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \
|
||||
"GIT","$(GIT_SUBMODULES)")
|
||||
endif
|
||||
|
||||
# 0. ensure the build tree is okay
|
||||
|
||||
# Check that we're not trying to do an out-of-tree build from
|
||||
@@ -83,17 +95,16 @@ config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh
|
||||
@if test -f meson-private/coredata.dat; then \
|
||||
./config.status --skip-meson; \
|
||||
else \
|
||||
./config.status; \
|
||||
./config.status && touch build.ninja.stamp; \
|
||||
fi
|
||||
|
||||
# 2. meson.stamp exists if meson has run at least once (so ninja reconfigure
|
||||
# works), but otherwise never needs to be updated
|
||||
|
||||
meson-private/coredata.dat: meson.stamp
|
||||
meson.stamp: config-host.mak
|
||||
@touch meson.stamp
|
||||
|
||||
# 3. ensure meson-generated build files are up-to-date
|
||||
# 3. ensure generated build files are up-to-date
|
||||
|
||||
ifneq ($(NINJA),)
|
||||
Makefile.ninja: build.ninja
|
||||
@@ -104,23 +115,15 @@ Makefile.ninja: build.ninja
|
||||
$(NINJA) -t query build.ninja | sed -n '1,/^ input:/d; /^ outputs:/q; s/$$/ \\/p'; \
|
||||
} > $@.tmp && mv $@.tmp $@
|
||||
-include Makefile.ninja
|
||||
endif
|
||||
|
||||
ifneq ($(MESON),)
|
||||
# The path to meson always points to pyvenv/bin/meson, but the absolute
|
||||
# paths could change. In that case, force a regeneration of build.ninja.
|
||||
# Note that this invocation of $(NINJA), just like when Make rebuilds
|
||||
# Makefiles, does not include -n.
|
||||
# A separate rule is needed for Makefile dependencies to avoid -n
|
||||
build.ninja: build.ninja.stamp
|
||||
$(build-files):
|
||||
build.ninja.stamp: meson.stamp $(build-files)
|
||||
@if test "$$(cat build.ninja.stamp)" = "$(MESON)" && test -n "$(NINJA)"; then \
|
||||
$(NINJA) build.ninja; \
|
||||
else \
|
||||
echo "$(MESON) setup --reconfigure $(SRC_PATH)"; \
|
||||
$(MESON) setup --reconfigure $(SRC_PATH); \
|
||||
fi && echo "$(MESON)" > $@
|
||||
$(NINJA) $(if $V,-v,) build.ninja && touch $@
|
||||
endif
|
||||
|
||||
ifneq ($(MESON),)
|
||||
Makefile.mtest: build.ninja scripts/mtest2make.py
|
||||
$(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@
|
||||
-include Makefile.mtest
|
||||
@@ -147,7 +150,7 @@ NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \
|
||||
ninja-cmd-goals = $(or $(MAKECMDGOALS), all)
|
||||
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
|
||||
|
||||
makefile-targets := build.ninja ctags TAGS cscope dist clean
|
||||
makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall
|
||||
# "ninja -t targets" also lists all prerequisites. If build system
|
||||
# files are marked as PHONY, however, Make will always try to execute
|
||||
# "ninja build.ninja".
|
||||
@@ -173,8 +176,10 @@ plugins:
|
||||
endif # $(CONFIG_PLUGIN)
|
||||
|
||||
else # config-host.mak does not exist
|
||||
config-host.mak:
|
||||
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||
$(error Please call configure before running make)
|
||||
@echo "Please call configure before running make!"
|
||||
@exit 1
|
||||
endif
|
||||
endif # config-host.mak does not exist
|
||||
|
||||
@@ -215,7 +220,7 @@ qemu-%.tar.bz2:
|
||||
|
||||
distclean: clean recurse-distclean
|
||||
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || :
|
||||
rm -f config-host.mak Makefile.prereqs
|
||||
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
|
||||
@@ -225,7 +230,7 @@ distclean: clean recurse-distclean
|
||||
rm -f Makefile.ninja Makefile.mtest build.ninja.stamp meson.stamp
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
rm -Rf .sdk qemu-bundle
|
||||
rm -Rf .sdk
|
||||
|
||||
find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \
|
||||
-type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \)
|
||||
|
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Lock to inhibit accelerator ioctls
|
||||
*
|
||||
* Copyright (c) 2022 Red Hat Inc.
|
||||
*
|
||||
* Author: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
*
|
||||
* 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/thread.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/accel-blocker.h"
|
||||
|
||||
static QemuLockCnt accel_in_ioctl_lock;
|
||||
static QemuEvent accel_in_ioctl_event;
|
||||
|
||||
void accel_blocker_init(void)
|
||||
{
|
||||
qemu_lockcnt_init(&accel_in_ioctl_lock);
|
||||
qemu_event_init(&accel_in_ioctl_event, false);
|
||||
}
|
||||
|
||||
void accel_ioctl_begin(void)
|
||||
{
|
||||
if (likely(qemu_mutex_iothread_locked())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* block if lock is taken in kvm_ioctl_inhibit_begin() */
|
||||
qemu_lockcnt_inc(&accel_in_ioctl_lock);
|
||||
}
|
||||
|
||||
void accel_ioctl_end(void)
|
||||
{
|
||||
if (likely(qemu_mutex_iothread_locked())) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&accel_in_ioctl_lock);
|
||||
/* change event to SET. If event was BUSY, wake up all waiters */
|
||||
qemu_event_set(&accel_in_ioctl_event);
|
||||
}
|
||||
|
||||
void accel_cpu_ioctl_begin(CPUState *cpu)
|
||||
{
|
||||
if (unlikely(qemu_mutex_iothread_locked())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* block if lock is taken in kvm_ioctl_inhibit_begin() */
|
||||
qemu_lockcnt_inc(&cpu->in_ioctl_lock);
|
||||
}
|
||||
|
||||
void accel_cpu_ioctl_end(CPUState *cpu)
|
||||
{
|
||||
if (unlikely(qemu_mutex_iothread_locked())) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&cpu->in_ioctl_lock);
|
||||
/* change event to SET. If event was BUSY, wake up all waiters */
|
||||
qemu_event_set(&accel_in_ioctl_event);
|
||||
}
|
||||
|
||||
static bool accel_has_to_wait(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
bool needs_to_wait = false;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
if (qemu_lockcnt_count(&cpu->in_ioctl_lock)) {
|
||||
/* exit the ioctl, if vcpu is running it */
|
||||
qemu_cpu_kick(cpu);
|
||||
needs_to_wait = true;
|
||||
}
|
||||
}
|
||||
|
||||
return needs_to_wait || qemu_lockcnt_count(&accel_in_ioctl_lock);
|
||||
}
|
||||
|
||||
void accel_ioctl_inhibit_begin(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
/*
|
||||
* We allow to inhibit only when holding the BQL, so we can identify
|
||||
* when an inhibitor wants to issue an ioctl easily.
|
||||
*/
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
|
||||
/* Block further invocations of the ioctls outside the BQL. */
|
||||
CPU_FOREACH(cpu) {
|
||||
qemu_lockcnt_lock(&cpu->in_ioctl_lock);
|
||||
}
|
||||
qemu_lockcnt_lock(&accel_in_ioctl_lock);
|
||||
|
||||
/* Keep waiting until there are running ioctls */
|
||||
while (true) {
|
||||
|
||||
/* Reset event to FREE. */
|
||||
qemu_event_reset(&accel_in_ioctl_event);
|
||||
|
||||
if (accel_has_to_wait()) {
|
||||
/*
|
||||
* If event is still FREE, and there are ioctls still in progress,
|
||||
* wait.
|
||||
*
|
||||
* If an ioctl finishes before qemu_event_wait(), it will change
|
||||
* the event state to SET. This will prevent qemu_event_wait() from
|
||||
* blocking, but it's not a problem because if other ioctls are
|
||||
* still running the loop will iterate once more and reset the event
|
||||
* status to FREE so that it can wait properly.
|
||||
*
|
||||
* If an ioctls finishes while qemu_event_wait() is blocking, then
|
||||
* it will be waken up, but also here the while loop makes sure
|
||||
* to re-enter the wait if there are other running ioctls.
|
||||
*/
|
||||
qemu_event_wait(&accel_in_ioctl_event);
|
||||
} else {
|
||||
/* No ioctl is running */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void accel_ioctl_inhibit_end(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
qemu_lockcnt_unlock(&accel_in_ioctl_lock);
|
||||
CPU_FOREACH(cpu) {
|
||||
qemu_lockcnt_unlock(&cpu->in_ioctl_lock);
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include "qemu/accel.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#include "accel-softmmu.h"
|
||||
|
||||
int accel_init_machine(AccelState *accel, MachineState *ms)
|
||||
|
@@ -52,7 +52,6 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/hvf.h"
|
||||
#include "sysemu/hvf_int.h"
|
||||
@@ -335,26 +334,18 @@ static int hvf_accel_init(MachineState *ms)
|
||||
s->slots[x].slot_id = x;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&s->hvf_sw_breakpoints);
|
||||
|
||||
hvf_state = s;
|
||||
memory_listener_register(&hvf_memory_listener, &address_space_memory);
|
||||
|
||||
return hvf_arch_init();
|
||||
}
|
||||
|
||||
static inline int hvf_gdbstub_sstep_flags(void)
|
||||
{
|
||||
return SSTEP_ENABLE | SSTEP_NOIRQ;
|
||||
}
|
||||
|
||||
static void hvf_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "HVF";
|
||||
ac->init_machine = hvf_accel_init;
|
||||
ac->allowed = &hvf_allowed;
|
||||
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
|
||||
}
|
||||
|
||||
static const TypeInfo hvf_accel_type = {
|
||||
@@ -404,8 +395,6 @@ static int hvf_init_vcpu(CPUState *cpu)
|
||||
cpu->vcpu_dirty = 1;
|
||||
assert_hvf_ok(r);
|
||||
|
||||
cpu->hvf->guest_debug_enabled = false;
|
||||
|
||||
return hvf_arch_init_vcpu(cpu);
|
||||
}
|
||||
|
||||
@@ -473,108 +462,6 @@ static void hvf_start_vcpu_thread(CPUState *cpu)
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
static int hvf_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
int err;
|
||||
|
||||
if (type == GDB_BREAKPOINT_SW) {
|
||||
bp = hvf_find_sw_breakpoint(cpu, addr);
|
||||
if (bp) {
|
||||
bp->use_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bp = g_new(struct hvf_sw_breakpoint, 1);
|
||||
bp->pc = addr;
|
||||
bp->use_count = 1;
|
||||
err = hvf_arch_insert_sw_breakpoint(cpu, bp);
|
||||
if (err) {
|
||||
g_free(bp);
|
||||
return err;
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_HEAD(&hvf_state->hvf_sw_breakpoints, bp, entry);
|
||||
} else {
|
||||
err = hvf_arch_insert_hw_breakpoint(addr, len, type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
err = hvf_update_guest_debug(cpu);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hvf_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
int err;
|
||||
|
||||
if (type == GDB_BREAKPOINT_SW) {
|
||||
bp = hvf_find_sw_breakpoint(cpu, addr);
|
||||
if (!bp) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (bp->use_count > 1) {
|
||||
bp->use_count--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = hvf_arch_remove_sw_breakpoint(cpu, bp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry);
|
||||
g_free(bp);
|
||||
} else {
|
||||
err = hvf_arch_remove_hw_breakpoint(addr, len, type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
err = hvf_update_guest_debug(cpu);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvf_remove_all_breakpoints(CPUState *cpu)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp, *next;
|
||||
CPUState *tmpcpu;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(bp, &hvf_state->hvf_sw_breakpoints, entry, next) {
|
||||
if (hvf_arch_remove_sw_breakpoint(cpu, bp) != 0) {
|
||||
/* Try harder to find a CPU that currently sees the breakpoint. */
|
||||
CPU_FOREACH(tmpcpu)
|
||||
{
|
||||
if (hvf_arch_remove_sw_breakpoint(tmpcpu, bp) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry);
|
||||
g_free(bp);
|
||||
}
|
||||
hvf_arch_remove_all_hw_breakpoints();
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
hvf_update_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
@@ -586,12 +473,6 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
|
||||
ops->synchronize_state = hvf_cpu_synchronize_state;
|
||||
ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm;
|
||||
|
||||
ops->insert_breakpoint = hvf_insert_breakpoint;
|
||||
ops->remove_breakpoint = hvf_remove_breakpoint;
|
||||
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
|
||||
ops->update_guest_debug = hvf_update_guest_debug;
|
||||
ops->supports_guest_debug = hvf_arch_supports_guest_debug;
|
||||
};
|
||||
static const TypeInfo hvf_accel_ops_type = {
|
||||
.name = ACCEL_OPS_NAME("hvf"),
|
||||
|
@@ -38,38 +38,9 @@ void assert_hvf_ok(hv_return_t ret)
|
||||
case HV_UNSUPPORTED:
|
||||
error_report("Error: HV_UNSUPPORTED");
|
||||
break;
|
||||
#if defined(MAC_OS_VERSION_11_0) && \
|
||||
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0
|
||||
case HV_DENIED:
|
||||
error_report("Error: HV_DENIED");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
error_report("Unknown Error");
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
|
||||
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
|
||||
if (bp->pc == pc) {
|
||||
return bp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hvf_sw_breakpoints_active(CPUState *cpu)
|
||||
{
|
||||
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
|
||||
}
|
||||
|
||||
int hvf_update_guest_debug(CPUState *cpu)
|
||||
{
|
||||
hvf_arch_update_guest_debug(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -86,13 +86,6 @@ static bool kvm_cpus_are_resettable(void)
|
||||
return !kvm_enabled() || kvm_cpu_check_are_resettable();
|
||||
}
|
||||
|
||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
||||
static int kvm_update_guest_debug_ops(CPUState *cpu)
|
||||
{
|
||||
return kvm_update_guest_debug(cpu, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
@@ -106,7 +99,6 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm;
|
||||
|
||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
||||
ops->update_guest_debug = kvm_update_guest_debug_ops;
|
||||
ops->supports_guest_debug = kvm_supports_guest_debug;
|
||||
ops->insert_breakpoint = kvm_insert_breakpoint;
|
||||
ops->remove_breakpoint = kvm_remove_breakpoint;
|
||||
|
@@ -31,7 +31,6 @@
|
||||
#include "sysemu/kvm_int.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/accel-blocker.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ram_addr.h"
|
||||
@@ -47,10 +46,9 @@
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "kvm-cpus.h"
|
||||
#include "sysemu/dirtylimit.h"
|
||||
#include "qemu/range.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/stats.h"
|
||||
#include "monitor/stats.h"
|
||||
|
||||
/* This check must be after config-host.h is included */
|
||||
#ifdef CONFIG_EVENTFD
|
||||
@@ -450,8 +448,6 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||
"kvm_init_vcpu: kvm_arch_init_vcpu failed (%lu)",
|
||||
kvm_arch_vcpu_id(cpu));
|
||||
}
|
||||
cpu->kvm_vcpu_stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -687,15 +683,6 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu)
|
||||
uint32_t ring_size = s->kvm_dirty_ring_size;
|
||||
uint32_t count = 0, fetch = cpu->kvm_fetch_index;
|
||||
|
||||
/*
|
||||
* It's possible that we race with vcpu creation code where the vcpu is
|
||||
* put onto the vcpus list but not yet initialized the dirty ring
|
||||
* structures. If so, skip it.
|
||||
*/
|
||||
if (!cpu->created) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(dirty_gfns && ring_size);
|
||||
trace_kvm_dirty_ring_reap_vcpu(cpu->cpu_index);
|
||||
|
||||
@@ -1305,7 +1292,6 @@ void kvm_set_max_memslot_size(hwaddr max_slot_size)
|
||||
kvm_max_slot_size = max_slot_size;
|
||||
}
|
||||
|
||||
/* Called with KVMMemoryListener.slots_lock held */
|
||||
static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section, bool add)
|
||||
{
|
||||
@@ -1340,12 +1326,14 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
ram = memory_region_get_ram_ptr(mr) + mr_offset;
|
||||
ram_start_offset = memory_region_get_ram_addr(mr) + mr_offset;
|
||||
|
||||
kvm_slots_lock();
|
||||
|
||||
if (!add) {
|
||||
do {
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
|
||||
if (!mem) {
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
/*
|
||||
@@ -1363,10 +1351,6 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
*/
|
||||
if (kvm_state->kvm_dirty_ring_size) {
|
||||
kvm_dirty_ring_reap_locked(kvm_state, NULL);
|
||||
if (kvm_state->kvm_dirty_ring_with_bitmap) {
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
kvm_slot_get_dirty_log(kvm_state, mem);
|
||||
}
|
||||
} else {
|
||||
kvm_slot_get_dirty_log(kvm_state, mem);
|
||||
}
|
||||
@@ -1387,7 +1371,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
start_addr += slot_size;
|
||||
size -= slot_size;
|
||||
} while (size);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* register the new slot */
|
||||
@@ -1412,6 +1396,9 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
ram += slot_size;
|
||||
size -= slot_size;
|
||||
} while (size);
|
||||
|
||||
out:
|
||||
kvm_slots_unlock();
|
||||
}
|
||||
|
||||
static void *kvm_dirty_ring_reaper_thread(void *data)
|
||||
@@ -1464,162 +1451,22 @@ static int kvm_dirty_ring_reaper_init(KVMState *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_dirty_ring_init(KVMState *s)
|
||||
{
|
||||
uint32_t ring_size = s->kvm_dirty_ring_size;
|
||||
uint64_t ring_bytes = ring_size * sizeof(struct kvm_dirty_gfn);
|
||||
unsigned int capability = KVM_CAP_DIRTY_LOG_RING;
|
||||
int ret;
|
||||
|
||||
s->kvm_dirty_ring_size = 0;
|
||||
s->kvm_dirty_ring_bytes = 0;
|
||||
|
||||
/* Bail if the dirty ring size isn't specified */
|
||||
if (!ring_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the max supported pages. Fall back to dirty logging mode
|
||||
* if the dirty ring isn't supported.
|
||||
*/
|
||||
ret = kvm_vm_check_extension(s, capability);
|
||||
if (ret <= 0) {
|
||||
capability = KVM_CAP_DIRTY_LOG_RING_ACQ_REL;
|
||||
ret = kvm_vm_check_extension(s, capability);
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
warn_report("KVM dirty ring not available, using bitmap method");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ring_bytes > ret) {
|
||||
error_report("KVM dirty ring size %" PRIu32 " too big "
|
||||
"(maximum is %ld). Please use a smaller value.",
|
||||
ring_size, (long)ret / sizeof(struct kvm_dirty_gfn));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = kvm_vm_enable_cap(s, capability, 0, ring_bytes);
|
||||
if (ret) {
|
||||
error_report("Enabling of KVM dirty ring failed: %s. "
|
||||
"Suggested minimum value is 1024.", strerror(-ret));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Enable the backup bitmap if it is supported */
|
||||
ret = kvm_vm_check_extension(s, KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP);
|
||||
if (ret > 0) {
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP, 0);
|
||||
if (ret) {
|
||||
error_report("Enabling of KVM dirty ring's backup bitmap failed: "
|
||||
"%s. ", strerror(-ret));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
s->kvm_dirty_ring_with_bitmap = true;
|
||||
}
|
||||
|
||||
s->kvm_dirty_ring_size = ring_size;
|
||||
s->kvm_dirty_ring_bytes = ring_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvm_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
|
||||
KVMMemoryUpdate *update;
|
||||
|
||||
update = g_new0(KVMMemoryUpdate, 1);
|
||||
update->section = *section;
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&kml->transaction_add, update, next);
|
||||
memory_region_ref(section->mr);
|
||||
kvm_set_phys_mem(kml, section, true);
|
||||
}
|
||||
|
||||
static void kvm_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
|
||||
KVMMemoryUpdate *update;
|
||||
|
||||
update = g_new0(KVMMemoryUpdate, 1);
|
||||
update->section = *section;
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&kml->transaction_del, update, next);
|
||||
}
|
||||
|
||||
static void kvm_region_commit(MemoryListener *listener)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener,
|
||||
listener);
|
||||
KVMMemoryUpdate *u1, *u2;
|
||||
bool need_inhibit = false;
|
||||
|
||||
if (QSIMPLEQ_EMPTY(&kml->transaction_add) &&
|
||||
QSIMPLEQ_EMPTY(&kml->transaction_del)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to be careful when regions to add overlap with ranges to remove.
|
||||
* We have to simulate atomic KVM memslot updates by making sure no ioctl()
|
||||
* is currently active.
|
||||
*
|
||||
* The lists are order by addresses, so it's easy to find overlaps.
|
||||
*/
|
||||
u1 = QSIMPLEQ_FIRST(&kml->transaction_del);
|
||||
u2 = QSIMPLEQ_FIRST(&kml->transaction_add);
|
||||
while (u1 && u2) {
|
||||
Range r1, r2;
|
||||
|
||||
range_init_nofail(&r1, u1->section.offset_within_address_space,
|
||||
int128_get64(u1->section.size));
|
||||
range_init_nofail(&r2, u2->section.offset_within_address_space,
|
||||
int128_get64(u2->section.size));
|
||||
|
||||
if (range_overlaps_range(&r1, &r2)) {
|
||||
need_inhibit = true;
|
||||
break;
|
||||
}
|
||||
if (range_lob(&r1) < range_lob(&r2)) {
|
||||
u1 = QSIMPLEQ_NEXT(u1, next);
|
||||
} else {
|
||||
u2 = QSIMPLEQ_NEXT(u2, next);
|
||||
}
|
||||
}
|
||||
|
||||
kvm_slots_lock();
|
||||
if (need_inhibit) {
|
||||
accel_ioctl_inhibit_begin();
|
||||
}
|
||||
|
||||
/* Remove all memslots before adding the new ones. */
|
||||
while (!QSIMPLEQ_EMPTY(&kml->transaction_del)) {
|
||||
u1 = QSIMPLEQ_FIRST(&kml->transaction_del);
|
||||
QSIMPLEQ_REMOVE_HEAD(&kml->transaction_del, next);
|
||||
|
||||
kvm_set_phys_mem(kml, &u1->section, false);
|
||||
memory_region_unref(u1->section.mr);
|
||||
|
||||
g_free(u1);
|
||||
}
|
||||
while (!QSIMPLEQ_EMPTY(&kml->transaction_add)) {
|
||||
u1 = QSIMPLEQ_FIRST(&kml->transaction_add);
|
||||
QSIMPLEQ_REMOVE_HEAD(&kml->transaction_add, next);
|
||||
|
||||
memory_region_ref(u1->section.mr);
|
||||
kvm_set_phys_mem(kml, &u1->section, true);
|
||||
|
||||
g_free(u1);
|
||||
}
|
||||
|
||||
if (need_inhibit) {
|
||||
accel_ioctl_inhibit_end();
|
||||
}
|
||||
kvm_slots_unlock();
|
||||
kvm_set_phys_mem(kml, section, false);
|
||||
memory_region_unref(section->mr);
|
||||
}
|
||||
|
||||
static void kvm_log_sync(MemoryListener *listener,
|
||||
@@ -1632,7 +1479,7 @@ static void kvm_log_sync(MemoryListener *listener,
|
||||
kvm_slots_unlock();
|
||||
}
|
||||
|
||||
static void kvm_log_sync_global(MemoryListener *l, bool last_stage)
|
||||
static void kvm_log_sync_global(MemoryListener *l)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(l, KVMMemoryListener, listener);
|
||||
KVMState *s = kvm_state;
|
||||
@@ -1651,12 +1498,6 @@ static void kvm_log_sync_global(MemoryListener *l, bool last_stage)
|
||||
mem = &kml->slots[i];
|
||||
if (mem->memory_size && mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
|
||||
if (s->kvm_dirty_ring_with_bitmap && last_stage &&
|
||||
kvm_slot_get_dirty_log(s, mem)) {
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is not needed by KVM_GET_DIRTY_LOG because the
|
||||
* ioctl will unconditionally overwrite the whole region.
|
||||
@@ -1769,12 +1610,8 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
kml->slots[i].slot = i;
|
||||
}
|
||||
|
||||
QSIMPLEQ_INIT(&kml->transaction_add);
|
||||
QSIMPLEQ_INIT(&kml->transaction_del);
|
||||
|
||||
kml->listener.region_add = kvm_region_add;
|
||||
kml->listener.region_del = kvm_region_del;
|
||||
kml->listener.commit = kvm_region_commit;
|
||||
kml->listener.log_start = kvm_log_start;
|
||||
kml->listener.log_stop = kvm_log_stop;
|
||||
kml->listener.priority = 10;
|
||||
@@ -2445,13 +2282,13 @@ static int kvm_init(MachineState *ms)
|
||||
static const char upgrade_note[] =
|
||||
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
|
||||
"(see http://sourceforge.net/projects/kvm).\n";
|
||||
const struct {
|
||||
struct {
|
||||
const char *name;
|
||||
int num;
|
||||
} num_cpus[] = {
|
||||
{ "SMP", ms->smp.cpus },
|
||||
{ "hotpluggable", ms->smp.max_cpus },
|
||||
{ /* end of list */ }
|
||||
{ NULL, }
|
||||
}, *nc = num_cpus;
|
||||
int soft_vcpus_limit, hard_vcpus_limit;
|
||||
KVMState *s;
|
||||
@@ -2473,7 +2310,6 @@ static int kvm_init(MachineState *ms)
|
||||
assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size());
|
||||
|
||||
s->sigmask_len = 8;
|
||||
accel_blocker_init();
|
||||
|
||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
||||
QTAILQ_INIT(&s->kvm_sw_breakpoints);
|
||||
@@ -2596,9 +2432,35 @@ static int kvm_init(MachineState *ms)
|
||||
* Enable KVM dirty ring if supported, otherwise fall back to
|
||||
* dirty logging mode
|
||||
*/
|
||||
ret = kvm_dirty_ring_init(s);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
if (s->kvm_dirty_ring_size > 0) {
|
||||
uint64_t ring_bytes;
|
||||
|
||||
ring_bytes = s->kvm_dirty_ring_size * sizeof(struct kvm_dirty_gfn);
|
||||
|
||||
/* Read the max supported pages */
|
||||
ret = kvm_vm_check_extension(s, KVM_CAP_DIRTY_LOG_RING);
|
||||
if (ret > 0) {
|
||||
if (ring_bytes > ret) {
|
||||
error_report("KVM dirty ring size %" PRIu32 " too big "
|
||||
"(maximum is %ld). Please use a smaller value.",
|
||||
s->kvm_dirty_ring_size,
|
||||
(long)ret / sizeof(struct kvm_dirty_gfn));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_DIRTY_LOG_RING, 0, ring_bytes);
|
||||
if (ret) {
|
||||
error_report("Enabling of KVM dirty ring failed: %s. "
|
||||
"Suggested minimum value is 1024.", strerror(-ret));
|
||||
goto err;
|
||||
}
|
||||
|
||||
s->kvm_dirty_ring_bytes = ring_bytes;
|
||||
} else {
|
||||
warn_report("KVM dirty ring not available, using bitmap method");
|
||||
s->kvm_dirty_ring_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3152,9 +3014,7 @@ int kvm_vm_ioctl(KVMState *s, int type, ...)
|
||||
va_end(ap);
|
||||
|
||||
trace_kvm_vm_ioctl(type, arg);
|
||||
accel_ioctl_begin();
|
||||
ret = ioctl(s->vmfd, type, arg);
|
||||
accel_ioctl_end();
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
}
|
||||
@@ -3172,9 +3032,7 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...)
|
||||
va_end(ap);
|
||||
|
||||
trace_kvm_vcpu_ioctl(cpu->cpu_index, type, arg);
|
||||
accel_cpu_ioctl_begin(cpu);
|
||||
ret = ioctl(cpu->kvm_fd, type, arg);
|
||||
accel_cpu_ioctl_end(cpu);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
}
|
||||
@@ -3192,9 +3050,7 @@ int kvm_device_ioctl(int fd, int type, ...)
|
||||
va_end(ap);
|
||||
|
||||
trace_kvm_device_ioctl(fd, type, arg);
|
||||
accel_ioctl_begin();
|
||||
ret = ioctl(fd, type, arg);
|
||||
accel_ioctl_end();
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
}
|
||||
@@ -3363,7 +3219,7 @@ bool kvm_supports_guest_debug(void)
|
||||
return kvm_has_guest_debug;
|
||||
}
|
||||
|
||||
int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len)
|
||||
int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
struct kvm_sw_breakpoint *bp;
|
||||
int err;
|
||||
@@ -3401,7 +3257,7 @@ int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len)
|
||||
int kvm_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
struct kvm_sw_breakpoint *bp;
|
||||
int err;
|
||||
@@ -3730,6 +3586,7 @@ static void kvm_set_dirty_ring_size(Object *obj, Visitor *v,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
Error *error = NULL;
|
||||
uint32_t value;
|
||||
|
||||
if (s->fd != -1) {
|
||||
@@ -3737,7 +3594,9 @@ static void kvm_set_dirty_ring_size(Object *obj, Visitor *v,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!visit_type_uint32(v, name, &value, errp)) {
|
||||
visit_type_uint32(v, name, &value, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
if (value & (value - 1)) {
|
||||
@@ -3759,12 +3618,8 @@ static void kvm_accel_instance_init(Object *obj)
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_AUTO;
|
||||
/* KVM dirty ring is by default off */
|
||||
s->kvm_dirty_ring_size = 0;
|
||||
s->kvm_dirty_ring_with_bitmap = false;
|
||||
s->notify_vmexit = NOTIFY_VMEXIT_OPTION_RUN;
|
||||
s->notify_window = 0;
|
||||
s->xen_version = 0;
|
||||
s->xen_gnttab_max_frames = 64;
|
||||
s->xen_evtchn_max_pirq = 256;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4009,7 +3864,7 @@ static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd
|
||||
|
||||
/* Read stats header */
|
||||
kvm_stats_header = &descriptors->kvm_stats_header;
|
||||
ret = pread(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header), 0);
|
||||
ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header));
|
||||
if (ret != sizeof(*kvm_stats_header)) {
|
||||
error_setg(errp, "KVM stats: failed to read stats header: "
|
||||
"expected %zu actual %zu",
|
||||
@@ -4040,8 +3895,7 @@ static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd
|
||||
}
|
||||
|
||||
static void query_stats(StatsResultList **result, StatsTarget target,
|
||||
strList *names, int stats_fd, CPUState *cpu,
|
||||
Error **errp)
|
||||
strList *names, int stats_fd, Error **errp)
|
||||
{
|
||||
struct kvm_stats_desc *kvm_stats_desc;
|
||||
struct kvm_stats_header *kvm_stats_header;
|
||||
@@ -4099,7 +3953,7 @@ static void query_stats(StatsResultList **result, StatsTarget target,
|
||||
break;
|
||||
case STATS_TARGET_VCPU:
|
||||
add_stats_entry(result, STATS_PROVIDER_KVM,
|
||||
cpu->parent_obj.canonical_path,
|
||||
current_cpu->parent_obj.canonical_path,
|
||||
stats_list);
|
||||
break;
|
||||
default:
|
||||
@@ -4136,9 +3990,10 @@ static void query_stats_schema(StatsSchemaList **result, StatsTarget target,
|
||||
add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list);
|
||||
}
|
||||
|
||||
static void query_stats_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args)
|
||||
static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
int stats_fd = cpu->kvm_vcpu_stats_fd;
|
||||
StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr;
|
||||
int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (stats_fd == -1) {
|
||||
@@ -4147,13 +4002,14 @@ static void query_stats_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args)
|
||||
return;
|
||||
}
|
||||
query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU,
|
||||
kvm_stats_args->names, stats_fd, cpu,
|
||||
kvm_stats_args->errp);
|
||||
kvm_stats_args->names, stats_fd, kvm_stats_args->errp);
|
||||
close(stats_fd);
|
||||
}
|
||||
|
||||
static void query_stats_schema_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args)
|
||||
static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
int stats_fd = cpu->kvm_vcpu_stats_fd;
|
||||
StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr;
|
||||
int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (stats_fd == -1) {
|
||||
@@ -4163,6 +4019,7 @@ static void query_stats_schema_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args)
|
||||
}
|
||||
query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd,
|
||||
kvm_stats_args->errp);
|
||||
close(stats_fd);
|
||||
}
|
||||
|
||||
static void query_stats_cb(StatsResultList **result, StatsTarget target,
|
||||
@@ -4180,7 +4037,7 @@ static void query_stats_cb(StatsResultList **result, StatsTarget target,
|
||||
error_setg_errno(errp, errno, "KVM stats: ioctl failed");
|
||||
return;
|
||||
}
|
||||
query_stats(result, target, names, stats_fd, NULL, errp);
|
||||
query_stats(result, target, names, stats_fd, errp);
|
||||
close(stats_fd);
|
||||
break;
|
||||
}
|
||||
@@ -4194,7 +4051,7 @@ static void query_stats_cb(StatsResultList **result, StatsTarget target,
|
||||
if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) {
|
||||
continue;
|
||||
}
|
||||
query_stats_vcpu(cpu, &stats_args);
|
||||
run_on_cpu(cpu, query_stats_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4220,6 +4077,6 @@ void query_stats_schemas_cb(StatsSchemaList **result, Error **errp)
|
||||
if (first_cpu) {
|
||||
stats_args.result.schema = result;
|
||||
stats_args.errp = errp;
|
||||
query_stats_schema_vcpu(first_cpu, &stats_args);
|
||||
run_on_cpu(first_cpu, query_stats_schema_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args));
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,8 @@ 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, vaddr addr, vaddr len);
|
||||
int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len);
|
||||
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 */
|
||||
|
@@ -1,5 +1,5 @@
|
||||
specific_ss.add(files('accel-common.c', 'accel-blocker.c'))
|
||||
system_ss.add(files('accel-softmmu.c'))
|
||||
specific_ss.add(files('accel-common.c'))
|
||||
softmmu_ss.add(files('accel-softmmu.c'))
|
||||
user_ss.add(files('accel-user.c'))
|
||||
|
||||
subdir('tcg')
|
||||
@@ -11,5 +11,10 @@ if have_system
|
||||
subdir('stubs')
|
||||
endif
|
||||
|
||||
# qtest
|
||||
system_ss.add(files('dummy-cpus.c'))
|
||||
dummy_ss = ss.source_set()
|
||||
dummy_ss.add(files(
|
||||
'dummy-cpus.c',
|
||||
))
|
||||
|
||||
specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: dummy_ss)
|
||||
specific_ss.add_all(when: ['CONFIG_XEN'], if_true: dummy_ss)
|
||||
|
@@ -1 +1 @@
|
||||
qtest_module_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: files('qtest.c'))
|
||||
qtest_module_ss.add(when: ['CONFIG_SOFTMMU'], if_true: files('qtest.c'))
|
||||
|
@@ -4,4 +4,4 @@ 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_SYSTEM_ONLY'], if_true: sysemu_stubs_ss)
|
||||
specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: sysemu_stubs_ss)
|
||||
|
@@ -11,14 +11,13 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/tb-flush.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
void tb_flush(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void tlb_set_dirty(CPUState *cpu, vaddr vaddr)
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,14 +25,14 @@ void tcg_flush_jmp_cache(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
int probe_access_flags(CPUArchState *env, vaddr addr, int size,
|
||||
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, vaddr addr, int size,
|
||||
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
||||
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
/* Handled by hardware accelerator. */
|
||||
|
@@ -13,12 +13,26 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
static void atomic_trace_rmw_post(CPUArchState *env, uint64_t addr,
|
||||
static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr,
|
||||
MemOpIdx oi)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_RW);
|
||||
}
|
||||
|
||||
#if HAVE_ATOMIC128
|
||||
static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr,
|
||||
MemOpIdx oi)
|
||||
{
|
||||
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,
|
||||
MemOpIdx oi)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Atomic helpers callable from TCG.
|
||||
* These have a common interface and all defer to cpu_atomic_*
|
||||
@@ -26,7 +40,7 @@ static void atomic_trace_rmw_post(CPUArchState *env, uint64_t addr,
|
||||
*/
|
||||
|
||||
#define CMPXCHG_HELPER(OP, TYPE) \
|
||||
TYPE HELPER(atomic_##OP)(CPUArchState *env, uint64_t addr, \
|
||||
TYPE HELPER(atomic_##OP)(CPUArchState *env, target_ulong addr, \
|
||||
TYPE oldv, TYPE newv, uint32_t oi) \
|
||||
{ return cpu_atomic_##OP##_mmu(env, addr, oldv, newv, oi, GETPC()); }
|
||||
|
||||
@@ -41,35 +55,10 @@ CMPXCHG_HELPER(cmpxchgq_be, uint64_t)
|
||||
CMPXCHG_HELPER(cmpxchgq_le, uint64_t)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMPXCHG128
|
||||
CMPXCHG_HELPER(cmpxchgo_be, Int128)
|
||||
CMPXCHG_HELPER(cmpxchgo_le, Int128)
|
||||
#endif
|
||||
|
||||
#undef CMPXCHG_HELPER
|
||||
|
||||
Int128 HELPER(nonatomic_cmpxchgo)(CPUArchState *env, uint64_t addr,
|
||||
Int128 cmpv, Int128 newv, uint32_t oi)
|
||||
{
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
uintptr_t ra = GETPC();
|
||||
Int128 oldv;
|
||||
|
||||
oldv = cpu_ld16_mmu(env, addr, oi, ra);
|
||||
if (int128_eq(oldv, cmpv)) {
|
||||
cpu_st16_mmu(env, addr, newv, oi, ra);
|
||||
} else {
|
||||
/* Even with comparison failure, still need a write cycle. */
|
||||
probe_write(env, addr, 16, get_mmuidx(oi), ra);
|
||||
}
|
||||
return oldv;
|
||||
#else
|
||||
g_assert_not_reached();
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ATOMIC_HELPER(OP, TYPE) \
|
||||
TYPE HELPER(glue(atomic_,OP))(CPUArchState *env, uint64_t addr, \
|
||||
TYPE HELPER(glue(atomic_,OP))(CPUArchState *env, target_ulong addr, \
|
||||
TYPE val, uint32_t oi) \
|
||||
{ return glue(glue(cpu_atomic_,OP),_mmu)(env, addr, val, oi, GETPC()); }
|
||||
|
||||
|
@@ -73,7 +73,8 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
DATA_TYPE ret;
|
||||
|
||||
#if DATA_SIZE == 16
|
||||
@@ -86,11 +87,38 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if DATA_SIZE < 16
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ, retaddr);
|
||||
DATA_TYPE val;
|
||||
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_ld_post(env, addr, oi);
|
||||
return val;
|
||||
}
|
||||
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_WRITE, retaddr);
|
||||
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_st_post(env, addr, oi);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
DATA_TYPE ret;
|
||||
|
||||
ret = qatomic_xchg__nocheck(haddr, val);
|
||||
@@ -103,8 +131,9 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
DATA_TYPE *haddr, ret; \
|
||||
haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
DATA_TYPE ret; \
|
||||
ret = qatomic_##X(haddr, val); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, oi); \
|
||||
@@ -134,8 +163,9 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
XDATA_TYPE *haddr, cmp, old, new, val = xval; \
|
||||
haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \
|
||||
XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
smp_mb(); \
|
||||
cmp = qatomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
@@ -158,7 +188,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
#endif /* DATA SIZE < 16 */
|
||||
#endif /* DATA SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
|
||||
@@ -176,7 +206,8 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
DATA_TYPE ret;
|
||||
|
||||
#if DATA_SIZE == 16
|
||||
@@ -189,11 +220,39 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
||||
#if DATA_SIZE < 16
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ, retaddr);
|
||||
DATA_TYPE val;
|
||||
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_ld_post(env, addr, oi);
|
||||
return BSWAP(val);
|
||||
}
|
||||
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_WRITE, retaddr);
|
||||
|
||||
val = BSWAP(val);
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_st_post(env, addr, oi);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
ABI_TYPE ret;
|
||||
|
||||
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
|
||||
@@ -206,8 +265,9 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
DATA_TYPE *haddr, ret; \
|
||||
haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
DATA_TYPE ret; \
|
||||
ret = qatomic_##X(haddr, BSWAP(val)); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, oi); \
|
||||
@@ -234,8 +294,9 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \
|
||||
haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, 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; \
|
||||
smp_mb(); \
|
||||
ldn = qatomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
@@ -265,7 +326,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||
#undef ADD
|
||||
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
#endif /* DATA_SIZE < 16 */
|
||||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
@@ -21,8 +21,6 @@
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qemu/plugin.h"
|
||||
#include "internal.h"
|
||||
|
||||
bool tcg_allowed;
|
||||
|
||||
@@ -67,8 +65,6 @@ void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
/* Undo the setting in cpu_tb_exec. */
|
||||
cpu->can_do_io = 1;
|
||||
/* Undo any setting in generated code. */
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
@@ -82,8 +78,6 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
||||
|
||||
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
|
||||
{
|
||||
/* Prevent looping if already executing in a serial context. */
|
||||
g_assert(!cpu_in_serial_context(cpu));
|
||||
cpu->exception_index = EXCP_ATOMIC;
|
||||
cpu_loop_exit_restore(cpu, pc);
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "qemu/osdep.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"
|
||||
@@ -27,6 +28,8 @@
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/compiler.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
@@ -36,9 +39,9 @@
|
||||
#include "sysemu/cpus.h"
|
||||
#include "exec/cpu-all.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "exec/replay-core.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "tb-jmp-cache.h"
|
||||
#include "tb-hash.h"
|
||||
#include "tb-context.h"
|
||||
@@ -62,8 +65,8 @@ typedef struct SyncClocks {
|
||||
#define MAX_DELAY_PRINT_RATE 2000000000LL
|
||||
#define MAX_NB_PRINTS 100
|
||||
|
||||
int64_t max_delay;
|
||||
int64_t max_advance;
|
||||
static int64_t max_delay;
|
||||
static int64_t max_advance;
|
||||
|
||||
static void align_clocks(SyncClocks *sc, CPUState *cpu)
|
||||
{
|
||||
@@ -159,7 +162,7 @@ uint32_t curr_cflags(CPUState *cpu)
|
||||
*/
|
||||
if (unlikely(cpu->singlestep_enabled)) {
|
||||
cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1;
|
||||
} else if (qatomic_read(&one_insn_per_tb)) {
|
||||
} else if (singlestep) {
|
||||
cflags |= CF_NO_GOTO_TB | 1;
|
||||
} else if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
cflags |= CF_NO_GOTO_TB;
|
||||
@@ -169,12 +172,13 @@ uint32_t curr_cflags(CPUState *cpu)
|
||||
}
|
||||
|
||||
struct tb_desc {
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
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)
|
||||
@@ -182,10 +186,11 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
||||
const TranslationBlock *tb = p;
|
||||
const struct tb_desc *desc = d;
|
||||
|
||||
if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) &&
|
||||
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) == desc->cflags) {
|
||||
/* check next page if needed */
|
||||
tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb);
|
||||
@@ -193,7 +198,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
||||
return true;
|
||||
} else {
|
||||
tb_page_addr_t phys_page1;
|
||||
vaddr virt_page1;
|
||||
target_ulong virt_page1;
|
||||
|
||||
/*
|
||||
* We know that the first page matched, and an otherwise valid TB
|
||||
@@ -214,8 +219,8 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
||||
return false;
|
||||
}
|
||||
|
||||
static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc,
|
||||
uint64_t cs_base, uint32_t flags,
|
||||
static TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
||||
target_ulong cs_base, uint32_t flags,
|
||||
uint32_t cflags)
|
||||
{
|
||||
tb_page_addr_t phys_pc;
|
||||
@@ -226,21 +231,22 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc,
|
||||
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, (cflags & CF_PCREL ? 0 : pc),
|
||||
flags, cs_base, cflags);
|
||||
h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : pc),
|
||||
flags, cflags, *cpu->trace_dstate);
|
||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
||||
}
|
||||
|
||||
/* Might cause an exception, so have a longjmp destination ready */
|
||||
static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc,
|
||||
uint64_t cs_base, uint32_t flags,
|
||||
uint32_t cflags)
|
||||
static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
uint32_t flags, uint32_t cflags)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
CPUJumpCache *jc;
|
||||
@@ -251,57 +257,35 @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc,
|
||||
|
||||
hash = tb_jmp_cache_hash_func(pc);
|
||||
jc = cpu->tb_jmp_cache;
|
||||
tb = tb_jmp_cache_get_tb(jc, hash);
|
||||
|
||||
if (cflags & CF_PCREL) {
|
||||
/* Use acquire to ensure current load of pc from jc. */
|
||||
tb = qatomic_load_acquire(&jc->array[hash].tb);
|
||||
|
||||
if (likely(tb &&
|
||||
jc->array[hash].pc == pc &&
|
||||
tb->cs_base == cs_base &&
|
||||
tb->flags == flags &&
|
||||
tb_cflags(tb) == cflags)) {
|
||||
return tb;
|
||||
}
|
||||
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
jc->array[hash].pc = pc;
|
||||
/* Ensure pc is written first. */
|
||||
qatomic_store_release(&jc->array[hash].tb, tb);
|
||||
} else {
|
||||
/* Use rcu_read to ensure current load of pc from *tb. */
|
||||
tb = qatomic_rcu_read(&jc->array[hash].tb);
|
||||
|
||||
if (likely(tb &&
|
||||
tb->pc == pc &&
|
||||
tb->cs_base == cs_base &&
|
||||
tb->flags == flags &&
|
||||
tb_cflags(tb) == cflags)) {
|
||||
return tb;
|
||||
}
|
||||
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
/* Use the pc value already stored in tb->pc. */
|
||||
qatomic_set(&jc->array[hash].tb, tb);
|
||||
if (likely(tb &&
|
||||
tb_jmp_cache_get_pc(jc, hash, tb) == pc &&
|
||||
tb->cs_base == cs_base &&
|
||||
tb->flags == flags &&
|
||||
tb->trace_vcpu_dstate == *cpu->trace_dstate &&
|
||||
tb_cflags(tb) == cflags)) {
|
||||
return tb;
|
||||
}
|
||||
|
||||
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
tb_jmp_cache_set(jc, hash, tb, pc);
|
||||
return tb;
|
||||
}
|
||||
|
||||
static void log_cpu_exec(vaddr pc, CPUState *cpu,
|
||||
static void log_cpu_exec(target_ulong pc, CPUState *cpu,
|
||||
const TranslationBlock *tb)
|
||||
{
|
||||
if (qemu_log_in_addr_range(pc)) {
|
||||
qemu_log_mask(CPU_LOG_EXEC,
|
||||
"Trace %d: %p [%08" PRIx64
|
||||
"/%" VADDR_PRIx "/%08x/%08x] %s\n",
|
||||
"Trace %d: %p [" TARGET_FMT_lx
|
||||
"/" TARGET_FMT_lx "/%08x/%08x] %s\n",
|
||||
cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc,
|
||||
tb->flags, tb->cflags, lookup_symbol(pc));
|
||||
|
||||
#if defined(DEBUG_DISAS)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
|
||||
FILE *logfile = qemu_log_trylock();
|
||||
if (logfile) {
|
||||
@@ -313,17 +297,15 @@ static void log_cpu_exec(vaddr pc, CPUState *cpu,
|
||||
#if defined(TARGET_I386)
|
||||
flags |= CPU_DUMP_CCOP;
|
||||
#endif
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) {
|
||||
flags |= CPU_DUMP_VPU;
|
||||
}
|
||||
cpu_dump_state(cpu, logfile, flags);
|
||||
qemu_log_unlock(logfile);
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc,
|
||||
static bool check_for_breakpoints_slow(CPUState *cpu, target_ulong pc,
|
||||
uint32_t *cflags)
|
||||
{
|
||||
CPUBreakpoint *bp;
|
||||
@@ -389,7 +371,7 @@ static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc,
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool check_for_breakpoints(CPUState *cpu, vaddr pc,
|
||||
static inline bool check_for_breakpoints(CPUState *cpu, target_ulong pc,
|
||||
uint32_t *cflags)
|
||||
{
|
||||
return unlikely(!QTAILQ_EMPTY(&cpu->breakpoints)) &&
|
||||
@@ -408,8 +390,7 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags, cflags;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
@@ -456,7 +437,6 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
|
||||
qemu_thread_jit_execute();
|
||||
ret = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
cpu->can_do_io = 1;
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
/*
|
||||
* TODO: Delay swapping back to the read-write region of the TB
|
||||
* until we actually need to modify the TB. The read-only copy,
|
||||
@@ -480,15 +460,15 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
|
||||
if (cc->tcg_ops->synchronize_from_tb) {
|
||||
cc->tcg_ops->synchronize_from_tb(cpu, last_tb);
|
||||
} else {
|
||||
tcg_debug_assert(!(tb_cflags(last_tb) & CF_PCREL));
|
||||
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)) {
|
||||
vaddr pc = log_pc(cpu, last_tb);
|
||||
target_ulong pc = log_pc(cpu, last_tb);
|
||||
if (qemu_log_in_addr_range(pc)) {
|
||||
qemu_log("Stopped execution of TB chain before %p [%"
|
||||
VADDR_PRIx "] %s\n",
|
||||
qemu_log("Stopped execution of TB chain before %p ["
|
||||
TARGET_FMT_lx "] %s\n",
|
||||
last_tb->tc.ptr, pc, lookup_symbol(pc));
|
||||
}
|
||||
}
|
||||
@@ -530,8 +510,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags, cflags;
|
||||
int tb_exit;
|
||||
|
||||
@@ -568,7 +547,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
cpu_tb_exec(cpu, tb, &tb_exit);
|
||||
cpu_exec_exit(cpu);
|
||||
} else {
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
clear_helper_retaddr();
|
||||
if (have_mmap_lock()) {
|
||||
mmap_unlock();
|
||||
@@ -578,6 +557,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
assert_no_pages_locked();
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -592,18 +572,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
|
||||
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
|
||||
{
|
||||
/*
|
||||
* Get the rx view of the structure, from which we find the
|
||||
* executable code address, and tb_target_set_jmp_target can
|
||||
* produce a pc-relative displacement to jmp_target_addr[n].
|
||||
*/
|
||||
const TranslationBlock *c_tb = tcg_splitwx_to_rx(tb);
|
||||
uintptr_t offset = tb->jmp_insn_offset[n];
|
||||
uintptr_t jmp_rx = (uintptr_t)tb->tc.ptr + offset;
|
||||
uintptr_t jmp_rw = jmp_rx - tcg_splitwx_diff;
|
||||
|
||||
tb->jmp_target_addr[n] = addr;
|
||||
tb_target_set_jmp_target(c_tb, n, jmp_rx, jmp_rw);
|
||||
if (TCG_TARGET_HAS_direct_jump) {
|
||||
uintptr_t offset = tb->jmp_target_arg[n];
|
||||
uintptr_t tc_ptr = (uintptr_t)tb->tc.ptr;
|
||||
uintptr_t jmp_rx = tc_ptr + offset;
|
||||
uintptr_t jmp_rw = jmp_rx - tcg_splitwx_diff;
|
||||
tb_target_set_jmp_target(tc_ptr, jmp_rx, jmp_rw, addr);
|
||||
} else {
|
||||
tb->jmp_target_arg[n] = addr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
@@ -779,7 +756,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
* Ensure zeroing happens before reading cpu->exit_request or
|
||||
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
|
||||
*/
|
||||
qatomic_set_mb(&cpu_neg(cpu)->icount_decr.u16.high, 0);
|
||||
qatomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0);
|
||||
|
||||
if (unlikely(qatomic_read(&cpu->interrupt_request))) {
|
||||
int interrupt_request;
|
||||
@@ -882,8 +859,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
}
|
||||
|
||||
static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
vaddr pc, TranslationBlock **last_tb,
|
||||
int *tb_exit)
|
||||
target_ulong pc,
|
||||
TranslationBlock **last_tb, int *tb_exit)
|
||||
{
|
||||
int32_t insns_left;
|
||||
|
||||
@@ -932,10 +909,64 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
|
||||
/* main execution loop */
|
||||
|
||||
static int __attribute__((noinline))
|
||||
cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
||||
int cpu_exec(CPUState *cpu)
|
||||
{
|
||||
int ret;
|
||||
SyncClocks sc = { 0 };
|
||||
|
||||
/* replay_interrupt may need current_cpu */
|
||||
current_cpu = cpu;
|
||||
|
||||
if (cpu_handle_halt(cpu)) {
|
||||
return EXCP_HALTED;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
cpu_exec_enter(cpu);
|
||||
|
||||
/* Calculate difference between guest clock and host clock.
|
||||
* This delay includes the delay of the last cycle, so
|
||||
* what we have to do is sleep until it is 0. As for the
|
||||
* advance/delay we gain here, we try to fix it next time.
|
||||
*/
|
||||
init_delay_params(&sc, cpu);
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
if (sigsetjmp(cpu->jmp_env, 0) != 0) {
|
||||
#if defined(__clang__)
|
||||
/*
|
||||
* Some compilers wrongly smash all local variables after
|
||||
* siglongjmp (the spec requires that only non-volatile locals
|
||||
* which are changed between the sigsetjmp and siglongjmp are
|
||||
* permitted to be trashed). There were bug reports for gcc
|
||||
* 4.5.0 and clang. The bug is fixed in all versions of gcc
|
||||
* that we support, but is still unfixed in clang:
|
||||
* https://bugs.llvm.org/show_bug.cgi?id=21183
|
||||
*
|
||||
* Reload an essential local variable here for those compilers.
|
||||
* Newer versions of gcc would complain about this code (-Wclobbered),
|
||||
* so we only perform the workaround for clang.
|
||||
*/
|
||||
cpu = current_cpu;
|
||||
#else
|
||||
/* Non-buggy compilers preserve this; assert the correct value. */
|
||||
g_assert(cpu == current_cpu);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
clear_helper_retaddr();
|
||||
if (have_mmap_lock()) {
|
||||
mmap_unlock();
|
||||
}
|
||||
#endif
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
|
||||
assert_no_pages_locked();
|
||||
}
|
||||
|
||||
/* if an exception is pending, we execute it here */
|
||||
while (!cpu_handle_exception(cpu, &ret)) {
|
||||
@@ -944,8 +975,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
||||
|
||||
while (!cpu_handle_interrupt(cpu, &last_tb)) {
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags, cflags;
|
||||
|
||||
cpu_get_tb_cpu_state(cpu->env_ptr, &pc, &cs_base, &flags);
|
||||
@@ -970,27 +1000,17 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
CPUJumpCache *jc;
|
||||
uint32_t h;
|
||||
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
mmap_unlock();
|
||||
|
||||
/*
|
||||
* We add the TB in the virtual pc hash table
|
||||
* for the fast lookup
|
||||
*/
|
||||
h = tb_jmp_cache_hash_func(pc);
|
||||
jc = cpu->tb_jmp_cache;
|
||||
if (cflags & CF_PCREL) {
|
||||
jc->array[h].pc = pc;
|
||||
/* Ensure pc is written first. */
|
||||
qatomic_store_release(&jc->array[h].tb, tb);
|
||||
} else {
|
||||
/* Use the pc value already stored in tb->pc. */
|
||||
qatomic_set(&jc->array[h].tb, tb);
|
||||
}
|
||||
tb_jmp_cache_set(cpu->tb_jmp_cache, h, tb, pc);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
@@ -1013,59 +1033,9 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
||||
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
align_clocks(sc, cpu);
|
||||
align_clocks(&sc, cpu);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpu_exec_setjmp(CPUState *cpu, SyncClocks *sc)
|
||||
{
|
||||
/* Prepare setjmp context for exception handling. */
|
||||
if (unlikely(sigsetjmp(cpu->jmp_env, 0) != 0)) {
|
||||
/* Non-buggy compilers preserve this; assert the correct value. */
|
||||
g_assert(cpu == current_cpu);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
clear_helper_retaddr();
|
||||
if (have_mmap_lock()) {
|
||||
mmap_unlock();
|
||||
}
|
||||
#endif
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
assert_no_pages_locked();
|
||||
}
|
||||
|
||||
return cpu_exec_loop(cpu, sc);
|
||||
}
|
||||
|
||||
int cpu_exec(CPUState *cpu)
|
||||
{
|
||||
int ret;
|
||||
SyncClocks sc = { 0 };
|
||||
|
||||
/* replay_interrupt may need current_cpu */
|
||||
current_cpu = cpu;
|
||||
|
||||
if (cpu_handle_halt(cpu)) {
|
||||
return EXCP_HALTED;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
cpu_exec_enter(cpu);
|
||||
|
||||
/*
|
||||
* Calculate difference between guest clock and host clock.
|
||||
* This delay includes the delay of the last cycle, so
|
||||
* what we have to do is sleep until it is 0. As for the
|
||||
* advance/delay we gain here, we try to fix it next time.
|
||||
*/
|
||||
init_delay_params(&sc, cpu);
|
||||
|
||||
ret = cpu_exec_setjmp(cpu, &sc);
|
||||
|
||||
cpu_exec_exit(cpu);
|
||||
rcu_read_unlock();
|
||||
@@ -1094,10 +1064,94 @@ void tcg_exec_realizefn(CPUState *cpu, Error **errp)
|
||||
/* 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 */
|
||||
|
||||
tlb_destroy(cpu);
|
||||
g_free_rcu(cpu->tb_jmp_cache, rcu);
|
||||
g_free(cpu->tb_jmp_cache);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
static void dump_drift_info(GString *buf)
|
||||
{
|
||||
if (!icount_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
|
||||
(cpu_get_clock() - icount_get()) / SCALE_MS);
|
||||
if (icount_align_option) {
|
||||
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 {
|
||||
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 */
|
||||
|
2161
accel/tcg/cputlb.c
2161
accel/tcg/cputlb.c
File diff suppressed because it is too large
Load Diff
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Debug information support.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/lockable.h"
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#include "debuginfo.h"
|
||||
|
||||
static QemuMutex lock;
|
||||
static Dwfl *dwfl;
|
||||
static const Dwfl_Callbacks dwfl_callbacks = {
|
||||
.find_elf = NULL,
|
||||
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||
.section_address = NULL,
|
||||
.debuginfo_path = NULL,
|
||||
};
|
||||
|
||||
__attribute__((constructor))
|
||||
static void debuginfo_init(void)
|
||||
{
|
||||
qemu_mutex_init(&lock);
|
||||
}
|
||||
|
||||
void debuginfo_report_elf(const char *name, int fd, uint64_t bias)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&lock);
|
||||
|
||||
if (dwfl) {
|
||||
dwfl_report_begin_add(dwfl);
|
||||
} else {
|
||||
dwfl = dwfl_begin(&dwfl_callbacks);
|
||||
}
|
||||
|
||||
if (dwfl) {
|
||||
dwfl_report_elf(dwfl, name, name, fd, bias, true);
|
||||
dwfl_report_end(dwfl, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void debuginfo_lock(void)
|
||||
{
|
||||
qemu_mutex_lock(&lock);
|
||||
}
|
||||
|
||||
void debuginfo_query(struct debuginfo_query *q, size_t n)
|
||||
{
|
||||
const char *symbol, *file;
|
||||
Dwfl_Module *dwfl_module;
|
||||
Dwfl_Line *dwfl_line;
|
||||
GElf_Off dwfl_offset;
|
||||
GElf_Sym dwfl_sym;
|
||||
size_t i;
|
||||
int line;
|
||||
|
||||
if (!dwfl) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
dwfl_module = dwfl_addrmodule(dwfl, q[i].address);
|
||||
if (!dwfl_module) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (q[i].flags & DEBUGINFO_SYMBOL) {
|
||||
symbol = dwfl_module_addrinfo(dwfl_module, q[i].address,
|
||||
&dwfl_offset, &dwfl_sym,
|
||||
NULL, NULL, NULL);
|
||||
if (symbol) {
|
||||
q[i].symbol = symbol;
|
||||
q[i].offset = dwfl_offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (q[i].flags & DEBUGINFO_LINE) {
|
||||
dwfl_line = dwfl_module_getsrc(dwfl_module, q[i].address);
|
||||
if (dwfl_line) {
|
||||
file = dwfl_lineinfo(dwfl_line, NULL, &line, 0, NULL, NULL);
|
||||
if (file) {
|
||||
q[i].file = file;
|
||||
q[i].line = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debuginfo_unlock(void)
|
||||
{
|
||||
qemu_mutex_unlock(&lock);
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Debug information support.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_TCG_DEBUGINFO_H
|
||||
#define ACCEL_TCG_DEBUGINFO_H
|
||||
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
/*
|
||||
* Debuginfo describing a certain address.
|
||||
*/
|
||||
struct debuginfo_query {
|
||||
uint64_t address; /* Input: address. */
|
||||
int flags; /* Input: debuginfo subset. */
|
||||
const char *symbol; /* Symbol that the address is part of. */
|
||||
uint64_t offset; /* Offset from the symbol. */
|
||||
const char *file; /* Source file associated with the address. */
|
||||
int line; /* Line number in the source file. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Debuginfo subsets.
|
||||
*/
|
||||
#define DEBUGINFO_SYMBOL BIT(1)
|
||||
#define DEBUGINFO_LINE BIT(2)
|
||||
|
||||
#if defined(CONFIG_TCG) && defined(CONFIG_LIBDW)
|
||||
/*
|
||||
* Load debuginfo for the specified guest ELF image.
|
||||
* Return true on success, false on failure.
|
||||
*/
|
||||
void debuginfo_report_elf(const char *name, int fd, uint64_t bias);
|
||||
|
||||
/*
|
||||
* Take the debuginfo lock.
|
||||
*/
|
||||
void debuginfo_lock(void);
|
||||
|
||||
/*
|
||||
* Fill each on N Qs with the debuginfo about Q->ADDRESS as specified by
|
||||
* Q->FLAGS:
|
||||
*
|
||||
* - DEBUGINFO_SYMBOL: update Q->SYMBOL and Q->OFFSET. If symbol debuginfo is
|
||||
* missing, then leave them as is.
|
||||
* - DEBUINFO_LINE: update Q->FILE and Q->LINE. If line debuginfo is missing,
|
||||
* then leave them as is.
|
||||
*
|
||||
* This function must be called under the debuginfo lock. The results can be
|
||||
* accessed only until the debuginfo lock is released.
|
||||
*/
|
||||
void debuginfo_query(struct debuginfo_query *q, size_t n);
|
||||
|
||||
/*
|
||||
* Release the debuginfo lock.
|
||||
*/
|
||||
void debuginfo_unlock(void);
|
||||
#else
|
||||
static inline void debuginfo_report_elf(const char *image_name, int image_fd,
|
||||
uint64_t load_bias)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debuginfo_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debuginfo_query(struct debuginfo_query *q, size_t n)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debuginfo_unlock(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
14
accel/tcg/hmp.c
Normal file
14
accel/tcg/hmp.c
Normal file
@@ -0,0 +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"
|
||||
|
||||
static void hmp_tcg_register(void)
|
||||
{
|
||||
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);
|
@@ -17,34 +17,89 @@
|
||||
* memory related structures are protected with mmap_lock.
|
||||
* In !user-mode we use per-page locks.
|
||||
*/
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
|
||||
#else
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
#define assert_memory_lock()
|
||||
#else
|
||||
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)
|
||||
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
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline void page_table_config_init(void) { }
|
||||
#else
|
||||
void page_table_config_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
|
||||
unsigned size,
|
||||
uintptr_t retaddr);
|
||||
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc,
|
||||
uint64_t cs_base, uint32_t flags,
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc,
|
||||
target_ulong cs_base, uint32_t flags,
|
||||
int cflags);
|
||||
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);
|
||||
@@ -55,61 +110,13 @@ 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 vaddr log_pc(CPUState *cpu, const TranslationBlock *tb)
|
||||
static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb)
|
||||
{
|
||||
if (tb_cflags(tb) & CF_PCREL) {
|
||||
return cpu->cc->get_pc(cpu);
|
||||
} else {
|
||||
return tb->pc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if CS is not running in parallel with other cpus, either
|
||||
* because there are no other cpus or we are within an exclusive context.
|
||||
*/
|
||||
static inline bool cpu_in_serial_context(CPUState *cs)
|
||||
{
|
||||
return !(cs->tcg_cflags & CF_PARALLEL) || cpu_in_exclusive_context(cs);
|
||||
}
|
||||
|
||||
extern int64_t max_delay;
|
||||
extern int64_t max_advance;
|
||||
|
||||
extern bool one_insn_per_tb;
|
||||
|
||||
/**
|
||||
* tcg_req_mo:
|
||||
* @type: TCGBar
|
||||
*
|
||||
* Filter @type to the barrier that is required for the guest
|
||||
* memory ordering vs the host memory ordering. A non-zero
|
||||
* result indicates that some barrier is required.
|
||||
*
|
||||
* If TCG_GUEST_DEFAULT_MO is not defined, assume that the
|
||||
* guest requires strict ordering.
|
||||
*
|
||||
* This is a macro so that it's constant even without optimization.
|
||||
*/
|
||||
#ifdef TCG_GUEST_DEFAULT_MO
|
||||
# define tcg_req_mo(type) \
|
||||
((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO)
|
||||
#if TARGET_TB_PCREL
|
||||
return cpu->cc->get_pc(cpu);
|
||||
#else
|
||||
# define tcg_req_mo(type) ((type) & ~TCG_TARGET_DEFAULT_MO)
|
||||
return tb_pc(tb);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* cpu_req_mo:
|
||||
* @type: TCGBar
|
||||
*
|
||||
* If tcg_req_mo indicates a barrier for @type is required
|
||||
* for the guest memory model, issue a host memory barrier.
|
||||
*/
|
||||
#define cpu_req_mo(type) \
|
||||
do { \
|
||||
if (tcg_req_mo(type)) { \
|
||||
smp_mb(); \
|
||||
} \
|
||||
} while (0)
|
||||
}
|
||||
|
||||
#endif /* ACCEL_TCG_INTERNAL_H */
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ 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_mmu(env, addr, oi, ra);
|
||||
return cpu_ldw_be_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
@@ -39,21 +39,21 @@ 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_mmu(env, addr, oi, ra);
|
||||
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_mmu(env, addr, oi, ra);
|
||||
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_mmu(env, addr, oi, ra);
|
||||
return cpu_ldw_le_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
@@ -66,14 +66,14 @@ 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_mmu(env, addr, oi, ra);
|
||||
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_mmu(env, addr, oi, ra);
|
||||
return cpu_ldq_le_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
@@ -87,42 +87,42 @@ 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_mmu(env, addr, val, oi, ra);
|
||||
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_mmu(env, addr, val, oi, ra);
|
||||
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_mmu(env, addr, val, oi, ra);
|
||||
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_mmu(env, addr, val, oi, ra);
|
||||
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_mmu(env, addr, val, oi, ra);
|
||||
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_mmu(env, addr, val, oi, ra);
|
||||
cpu_stq_le_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
/*--------------------------*/
|
||||
|
@@ -10,18 +10,16 @@ tcg_ss.add(files(
|
||||
'translator.c',
|
||||
))
|
||||
tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
|
||||
tcg_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.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')])
|
||||
tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
|
||||
tcg_ss.add(when: 'CONFIG_LINUX', if_true: files('perf.c'))
|
||||
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
|
||||
|
||||
specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files(
|
||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files(
|
||||
'cputlb.c',
|
||||
'monitor.c',
|
||||
'hmp.c',
|
||||
))
|
||||
|
||||
tcg_module_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files(
|
||||
tcg_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files(
|
||||
'tcg-accel-ops.c',
|
||||
'tcg-accel-ops-mttcg.c',
|
||||
'tcg-accel-ops-icount.c',
|
||||
|
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*
|
||||
* QEMU TCG monitor
|
||||
*
|
||||
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/type-helpers.h"
|
||||
#include "qapi/qapi-commands-machine.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
static void dump_drift_info(GString *buf)
|
||||
{
|
||||
if (!icount_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
|
||||
(cpu_get_clock() - icount_get()) / SCALE_MS);
|
||||
if (icount_align_option) {
|
||||
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 {
|
||||
g_string_append_printf(buf, "Max guest delay NA\n");
|
||||
g_string_append_printf(buf, "Max guest advance NA\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_accel_info(GString *buf)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
|
||||
"one-insn-per-tb",
|
||||
&error_fatal);
|
||||
|
||||
g_string_append_printf(buf, "Accelerator settings:\n");
|
||||
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
|
||||
one_insn_per_tb ? "on" : "off");
|
||||
}
|
||||
|
||||
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_accel_info(buf);
|
||||
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);
|
||||
}
|
||||
|
||||
static void hmp_tcg_register(void)
|
||||
{
|
||||
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);
|
386
accel/tcg/perf.c
386
accel/tcg/perf.c
@@ -1,386 +0,0 @@
|
||||
/*
|
||||
* Linux perf perf-<pid>.map and jit-<pid>.dump integration.
|
||||
*
|
||||
* The jitdump spec can be found at [1].
|
||||
*
|
||||
* [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/tools/perf/Documentation/jitdump-specification.txt
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "elf.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
#include "debuginfo.h"
|
||||
#include "perf.h"
|
||||
|
||||
static FILE *safe_fopen_w(const char *path)
|
||||
{
|
||||
int saved_errno;
|
||||
FILE *f;
|
||||
int fd;
|
||||
|
||||
/* Delete the old file, if any. */
|
||||
unlink(path);
|
||||
|
||||
/* Avoid symlink attacks by using O_CREAT | O_EXCL. */
|
||||
fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert fd to FILE*. */
|
||||
f = fdopen(fd, "w");
|
||||
if (f == NULL) {
|
||||
saved_errno = errno;
|
||||
close(fd);
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static FILE *perfmap;
|
||||
|
||||
void perf_enable_perfmap(void)
|
||||
{
|
||||
char map_file[32];
|
||||
|
||||
snprintf(map_file, sizeof(map_file), "/tmp/perf-%d.map", getpid());
|
||||
perfmap = safe_fopen_w(map_file);
|
||||
if (perfmap == NULL) {
|
||||
warn_report("Could not open %s: %s, proceeding without perfmap",
|
||||
map_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* Get PC and size of code JITed for guest instruction #INSN. */
|
||||
static void get_host_pc_size(uintptr_t *host_pc, uint16_t *host_size,
|
||||
const void *start, size_t insn)
|
||||
{
|
||||
uint16_t start_off = insn ? tcg_ctx->gen_insn_end_off[insn - 1] : 0;
|
||||
|
||||
if (host_pc) {
|
||||
*host_pc = (uintptr_t)start + start_off;
|
||||
}
|
||||
if (host_size) {
|
||||
*host_size = tcg_ctx->gen_insn_end_off[insn] - start_off;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *pretty_symbol(const struct debuginfo_query *q, size_t *len)
|
||||
{
|
||||
static __thread char buf[64];
|
||||
int tmp;
|
||||
|
||||
if (!q->symbol) {
|
||||
tmp = snprintf(buf, sizeof(buf), "guest-0x%"PRIx64, q->address);
|
||||
if (len) {
|
||||
*len = MIN(tmp + 1, sizeof(buf));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (!q->offset) {
|
||||
if (len) {
|
||||
*len = strlen(q->symbol) + 1;
|
||||
}
|
||||
return q->symbol;
|
||||
}
|
||||
|
||||
tmp = snprintf(buf, sizeof(buf), "%s+0x%"PRIx64, q->symbol, q->offset);
|
||||
if (len) {
|
||||
*len = MIN(tmp + 1, sizeof(buf));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void write_perfmap_entry(const void *start, size_t insn,
|
||||
const struct debuginfo_query *q)
|
||||
{
|
||||
uint16_t host_size;
|
||||
uintptr_t host_pc;
|
||||
|
||||
get_host_pc_size(&host_pc, &host_size, start, insn);
|
||||
fprintf(perfmap, "%"PRIxPTR" %"PRIx16" %s\n",
|
||||
host_pc, host_size, pretty_symbol(q, NULL));
|
||||
}
|
||||
|
||||
static FILE *jitdump;
|
||||
static size_t perf_marker_size;
|
||||
static void *perf_marker = MAP_FAILED;
|
||||
|
||||
#define JITHEADER_MAGIC 0x4A695444
|
||||
#define JITHEADER_VERSION 1
|
||||
|
||||
struct jitheader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t total_size;
|
||||
uint32_t elf_mach;
|
||||
uint32_t pad1;
|
||||
uint32_t pid;
|
||||
uint64_t timestamp;
|
||||
uint64_t flags;
|
||||
};
|
||||
|
||||
enum jit_record_type {
|
||||
JIT_CODE_LOAD = 0,
|
||||
JIT_CODE_DEBUG_INFO = 2,
|
||||
};
|
||||
|
||||
struct jr_prefix {
|
||||
uint32_t id;
|
||||
uint32_t total_size;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct jr_code_load {
|
||||
struct jr_prefix p;
|
||||
|
||||
uint32_t pid;
|
||||
uint32_t tid;
|
||||
uint64_t vma;
|
||||
uint64_t code_addr;
|
||||
uint64_t code_size;
|
||||
uint64_t code_index;
|
||||
};
|
||||
|
||||
struct debug_entry {
|
||||
uint64_t addr;
|
||||
int lineno;
|
||||
int discrim;
|
||||
const char name[];
|
||||
};
|
||||
|
||||
struct jr_code_debug_info {
|
||||
struct jr_prefix p;
|
||||
|
||||
uint64_t code_addr;
|
||||
uint64_t nr_entry;
|
||||
struct debug_entry entries[];
|
||||
};
|
||||
|
||||
static uint32_t get_e_machine(void)
|
||||
{
|
||||
Elf64_Ehdr elf_header;
|
||||
FILE *exe;
|
||||
size_t n;
|
||||
|
||||
QEMU_BUILD_BUG_ON(offsetof(Elf32_Ehdr, e_machine) !=
|
||||
offsetof(Elf64_Ehdr, e_machine));
|
||||
|
||||
exe = fopen("/proc/self/exe", "r");
|
||||
if (exe == NULL) {
|
||||
return EM_NONE;
|
||||
}
|
||||
|
||||
n = fread(&elf_header, sizeof(elf_header), 1, exe);
|
||||
fclose(exe);
|
||||
if (n != 1) {
|
||||
return EM_NONE;
|
||||
}
|
||||
|
||||
return elf_header.e_machine;
|
||||
}
|
||||
|
||||
void perf_enable_jitdump(void)
|
||||
{
|
||||
struct jitheader header;
|
||||
char jitdump_file[32];
|
||||
|
||||
if (!use_rt_clock) {
|
||||
warn_report("CLOCK_MONOTONIC is not available, proceeding without jitdump");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(jitdump_file, sizeof(jitdump_file), "jit-%d.dump", getpid());
|
||||
jitdump = safe_fopen_w(jitdump_file);
|
||||
if (jitdump == NULL) {
|
||||
warn_report("Could not open %s: %s, proceeding without jitdump",
|
||||
jitdump_file, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* `perf inject` will see that the mapped file name in the corresponding
|
||||
* PERF_RECORD_MMAP or PERF_RECORD_MMAP2 event is of the form jit-%d.dump
|
||||
* and will process it as a jitdump file.
|
||||
*/
|
||||
perf_marker_size = qemu_real_host_page_size();
|
||||
perf_marker = mmap(NULL, perf_marker_size, PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE, fileno(jitdump), 0);
|
||||
if (perf_marker == MAP_FAILED) {
|
||||
warn_report("Could not map %s: %s, proceeding without jitdump",
|
||||
jitdump_file, strerror(errno));
|
||||
fclose(jitdump);
|
||||
jitdump = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
header.magic = JITHEADER_MAGIC;
|
||||
header.version = JITHEADER_VERSION;
|
||||
header.total_size = sizeof(header);
|
||||
header.elf_mach = get_e_machine();
|
||||
header.pad1 = 0;
|
||||
header.pid = getpid();
|
||||
header.timestamp = get_clock();
|
||||
header.flags = 0;
|
||||
fwrite(&header, sizeof(header), 1, jitdump);
|
||||
}
|
||||
|
||||
void perf_report_prologue(const void *start, size_t size)
|
||||
{
|
||||
if (perfmap) {
|
||||
fprintf(perfmap, "%"PRIxPTR" %zx tcg-prologue-buffer\n",
|
||||
(uintptr_t)start, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write a JIT_CODE_DEBUG_INFO jitdump entry. */
|
||||
static void write_jr_code_debug_info(const void *start,
|
||||
const struct debuginfo_query *q,
|
||||
size_t icount)
|
||||
{
|
||||
struct jr_code_debug_info rec;
|
||||
struct debug_entry ent;
|
||||
uintptr_t host_pc;
|
||||
int insn;
|
||||
|
||||
/* Write the header. */
|
||||
rec.p.id = JIT_CODE_DEBUG_INFO;
|
||||
rec.p.total_size = sizeof(rec) + sizeof(ent) + 1;
|
||||
rec.p.timestamp = get_clock();
|
||||
rec.code_addr = (uintptr_t)start;
|
||||
rec.nr_entry = 1;
|
||||
for (insn = 0; insn < icount; insn++) {
|
||||
if (q[insn].file) {
|
||||
rec.p.total_size += sizeof(ent) + strlen(q[insn].file) + 1;
|
||||
rec.nr_entry++;
|
||||
}
|
||||
}
|
||||
fwrite(&rec, sizeof(rec), 1, jitdump);
|
||||
|
||||
/* Write the main debug entries. */
|
||||
for (insn = 0; insn < icount; insn++) {
|
||||
if (q[insn].file) {
|
||||
get_host_pc_size(&host_pc, NULL, start, insn);
|
||||
ent.addr = host_pc;
|
||||
ent.lineno = q[insn].line;
|
||||
ent.discrim = 0;
|
||||
fwrite(&ent, sizeof(ent), 1, jitdump);
|
||||
fwrite(q[insn].file, strlen(q[insn].file) + 1, 1, jitdump);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the trailing debug_entry. */
|
||||
ent.addr = (uintptr_t)start + tcg_ctx->gen_insn_end_off[icount - 1];
|
||||
ent.lineno = 0;
|
||||
ent.discrim = 0;
|
||||
fwrite(&ent, sizeof(ent), 1, jitdump);
|
||||
fwrite("", 1, 1, jitdump);
|
||||
}
|
||||
|
||||
/* Write a JIT_CODE_LOAD jitdump entry. */
|
||||
static void write_jr_code_load(const void *start, uint16_t host_size,
|
||||
const struct debuginfo_query *q)
|
||||
{
|
||||
static uint64_t code_index;
|
||||
struct jr_code_load rec;
|
||||
const char *symbol;
|
||||
size_t symbol_size;
|
||||
|
||||
symbol = pretty_symbol(q, &symbol_size);
|
||||
rec.p.id = JIT_CODE_LOAD;
|
||||
rec.p.total_size = sizeof(rec) + symbol_size + host_size;
|
||||
rec.p.timestamp = get_clock();
|
||||
rec.pid = getpid();
|
||||
rec.tid = qemu_get_thread_id();
|
||||
rec.vma = (uintptr_t)start;
|
||||
rec.code_addr = (uintptr_t)start;
|
||||
rec.code_size = host_size;
|
||||
rec.code_index = code_index++;
|
||||
fwrite(&rec, sizeof(rec), 1, jitdump);
|
||||
fwrite(symbol, symbol_size, 1, jitdump);
|
||||
fwrite(start, host_size, 1, jitdump);
|
||||
}
|
||||
|
||||
void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
|
||||
const void *start)
|
||||
{
|
||||
struct debuginfo_query *q;
|
||||
size_t insn, start_words;
|
||||
uint64_t *gen_insn_data;
|
||||
|
||||
if (!perfmap && !jitdump) {
|
||||
return;
|
||||
}
|
||||
|
||||
q = g_try_malloc0_n(tb->icount, sizeof(*q));
|
||||
if (!q) {
|
||||
return;
|
||||
}
|
||||
|
||||
debuginfo_lock();
|
||||
|
||||
/* Query debuginfo for each guest instruction. */
|
||||
gen_insn_data = tcg_ctx->gen_insn_data;
|
||||
start_words = tcg_ctx->insn_start_words;
|
||||
|
||||
for (insn = 0; insn < tb->icount; insn++) {
|
||||
/* FIXME: This replicates the restore_state_to_opc() logic. */
|
||||
q[insn].address = gen_insn_data[insn * start_words + 0];
|
||||
if (tb_cflags(tb) & CF_PCREL) {
|
||||
q[insn].address |= (guest_pc & TARGET_PAGE_MASK);
|
||||
} else {
|
||||
#if defined(TARGET_I386)
|
||||
q[insn].address -= tb->cs_base;
|
||||
#endif
|
||||
}
|
||||
q[insn].flags = DEBUGINFO_SYMBOL | (jitdump ? DEBUGINFO_LINE : 0);
|
||||
}
|
||||
debuginfo_query(q, tb->icount);
|
||||
|
||||
/* Emit perfmap entries if needed. */
|
||||
if (perfmap) {
|
||||
flockfile(perfmap);
|
||||
for (insn = 0; insn < tb->icount; insn++) {
|
||||
write_perfmap_entry(start, insn, &q[insn]);
|
||||
}
|
||||
funlockfile(perfmap);
|
||||
}
|
||||
|
||||
/* Emit jitdump entries if needed. */
|
||||
if (jitdump) {
|
||||
flockfile(jitdump);
|
||||
write_jr_code_debug_info(start, q, tb->icount);
|
||||
write_jr_code_load(start, tcg_ctx->gen_insn_end_off[tb->icount - 1],
|
||||
q);
|
||||
funlockfile(jitdump);
|
||||
}
|
||||
|
||||
debuginfo_unlock();
|
||||
g_free(q);
|
||||
}
|
||||
|
||||
void perf_exit(void)
|
||||
{
|
||||
if (perfmap) {
|
||||
fclose(perfmap);
|
||||
perfmap = NULL;
|
||||
}
|
||||
|
||||
if (perf_marker != MAP_FAILED) {
|
||||
munmap(perf_marker, perf_marker_size);
|
||||
perf_marker = MAP_FAILED;
|
||||
}
|
||||
|
||||
if (jitdump) {
|
||||
fclose(jitdump);
|
||||
jitdump = NULL;
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Linux perf perf-<pid>.map and jit-<pid>.dump integration.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_TCG_PERF_H
|
||||
#define ACCEL_TCG_PERF_H
|
||||
|
||||
#if defined(CONFIG_TCG) && defined(CONFIG_LINUX)
|
||||
/* Start writing perf-<pid>.map. */
|
||||
void perf_enable_perfmap(void);
|
||||
|
||||
/* Start writing jit-<pid>.dump. */
|
||||
void perf_enable_jitdump(void);
|
||||
|
||||
/* Add information about TCG prologue to profiler maps. */
|
||||
void perf_report_prologue(const void *start, size_t size);
|
||||
|
||||
/* Add information about JITted guest code to profiler maps. */
|
||||
void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
|
||||
const void *start);
|
||||
|
||||
/* Stop writing perf-<pid>.map and/or jit-<pid>.dump. */
|
||||
void perf_exit(void);
|
||||
#else
|
||||
static inline void perf_enable_perfmap(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void perf_enable_jitdump(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void perf_report_prologue(const void *start, size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void perf_report_code(uint64_t guest_pc, TranslationBlock *tb,
|
||||
const void *start)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void perf_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@@ -43,18 +43,11 @@
|
||||
* CPU's index into a TCG temp, since the first callback did it already.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg/tcg-temp-internal.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
|
||||
#define HELPER_H "accel/tcg/plugin-helpers.h"
|
||||
#include "exec/helper-info.c.inc"
|
||||
#undef HELPER_H
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
# define CONFIG_SOFTMMU_GATE 1
|
||||
@@ -98,12 +91,30 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
|
||||
void *userdata)
|
||||
{ }
|
||||
|
||||
static void do_gen_mem_cb(TCGv vaddr, uint32_t info)
|
||||
{
|
||||
TCGv_i32 cpu_index = tcg_temp_new_i32();
|
||||
TCGv_i32 meminfo = tcg_const_i32(info);
|
||||
TCGv_i64 vaddr64 = tcg_temp_new_i64();
|
||||
TCGv_ptr udata = tcg_const_ptr(NULL);
|
||||
|
||||
tcg_gen_ld_i32(cpu_index, cpu_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
tcg_gen_extu_tl_i64(vaddr64, vaddr);
|
||||
|
||||
gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata);
|
||||
|
||||
tcg_temp_free_ptr(udata);
|
||||
tcg_temp_free_i64(vaddr64);
|
||||
tcg_temp_free_i32(meminfo);
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
}
|
||||
|
||||
static void gen_empty_udata_cb(void)
|
||||
{
|
||||
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
|
||||
TCGv_ptr udata = tcg_temp_ebb_new_ptr();
|
||||
TCGv_i32 cpu_index = tcg_temp_new_i32();
|
||||
TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */
|
||||
|
||||
tcg_gen_movi_ptr(udata, 0);
|
||||
tcg_gen_ld_i32(cpu_index, cpu_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
|
||||
@@ -118,10 +129,9 @@ static void gen_empty_udata_cb(void)
|
||||
*/
|
||||
static void gen_empty_inline_cb(void)
|
||||
{
|
||||
TCGv_i64 val = tcg_temp_ebb_new_i64();
|
||||
TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
|
||||
TCGv_i64 val = tcg_temp_new_i64();
|
||||
TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */
|
||||
|
||||
tcg_gen_movi_ptr(ptr, 0);
|
||||
tcg_gen_ld_i64(val, ptr, 0);
|
||||
/* pass an immediate != 0 so that it doesn't get optimized away */
|
||||
tcg_gen_addi_i64(val, val, 0xdeadface);
|
||||
@@ -130,22 +140,9 @@ static void gen_empty_inline_cb(void)
|
||||
tcg_temp_free_i64(val);
|
||||
}
|
||||
|
||||
static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
|
||||
static void gen_empty_mem_cb(TCGv addr, uint32_t info)
|
||||
{
|
||||
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
|
||||
TCGv_i32 meminfo = tcg_temp_ebb_new_i32();
|
||||
TCGv_ptr udata = tcg_temp_ebb_new_ptr();
|
||||
|
||||
tcg_gen_movi_i32(meminfo, info);
|
||||
tcg_gen_movi_ptr(udata, 0);
|
||||
tcg_gen_ld_i32(cpu_index, cpu_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
|
||||
gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata);
|
||||
|
||||
tcg_temp_free_ptr(udata);
|
||||
tcg_temp_free_i32(meminfo);
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
do_gen_mem_cb(addr, info);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -154,9 +151,9 @@ static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
|
||||
*/
|
||||
static void gen_empty_mem_helper(void)
|
||||
{
|
||||
TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
|
||||
TCGv_ptr ptr;
|
||||
|
||||
tcg_gen_movi_ptr(ptr, 0);
|
||||
ptr = tcg_const_ptr(NULL);
|
||||
tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) -
|
||||
offsetof(ArchCPU, env));
|
||||
tcg_temp_free_ptr(ptr);
|
||||
@@ -200,17 +197,35 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from)
|
||||
}
|
||||
}
|
||||
|
||||
void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info)
|
||||
union mem_gen_fn {
|
||||
void (*mem_fn)(TCGv, uint32_t);
|
||||
void (*inline_fn)(void);
|
||||
};
|
||||
|
||||
static void gen_mem_wrapped(enum plugin_gen_cb type,
|
||||
const union mem_gen_fn *f, TCGv addr,
|
||||
uint32_t info, bool is_mem)
|
||||
{
|
||||
enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
|
||||
|
||||
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw);
|
||||
gen_empty_mem_cb(addr, info);
|
||||
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, rw);
|
||||
if (is_mem) {
|
||||
f->mem_fn(addr, info);
|
||||
} else {
|
||||
f->inline_fn();
|
||||
}
|
||||
tcg_gen_plugin_cb_end();
|
||||
}
|
||||
|
||||
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw);
|
||||
gen_empty_inline_cb();
|
||||
tcg_gen_plugin_cb_end();
|
||||
void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info)
|
||||
{
|
||||
union mem_gen_fn fn;
|
||||
|
||||
fn.mem_fn = gen_empty_mem_cb;
|
||||
gen_mem_wrapped(PLUGIN_GEN_CB_MEM, &fn, addr, info, true);
|
||||
|
||||
fn.inline_fn = gen_empty_inline_cb;
|
||||
gen_mem_wrapped(PLUGIN_GEN_CB_INLINE, &fn, 0, info, false);
|
||||
}
|
||||
|
||||
static TCGOp *find_op(TCGOp *op, TCGOpcode opc)
|
||||
@@ -243,13 +258,10 @@ static TCGOp *rm_ops(TCGOp *op)
|
||||
|
||||
static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
TCGOp *old_op = QTAILQ_NEXT(*begin_op, link);
|
||||
unsigned nargs = old_op->nargs;
|
||||
|
||||
*begin_op = old_op;
|
||||
op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs);
|
||||
memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs);
|
||||
|
||||
*begin_op = QTAILQ_NEXT(*begin_op, link);
|
||||
tcg_debug_assert(*begin_op);
|
||||
op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc);
|
||||
memcpy(op->args, (*begin_op)->args, sizeof(op->args));
|
||||
return op;
|
||||
}
|
||||
|
||||
@@ -260,6 +272,33 @@ static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc)
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* mov_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i32);
|
||||
/* mov_i32 w/ $0 */
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i32);
|
||||
} else {
|
||||
/* extu_i32_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_extu_i32_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* 2x mov_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i32);
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i32);
|
||||
} else {
|
||||
/* mov_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
@@ -274,6 +313,18 @@ static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TARGET_LONG_BITS == 32) {
|
||||
/* extu_i32_i64 */
|
||||
op = copy_extu_i32_i64(begin_op, op);
|
||||
} else {
|
||||
/* mov_i64 */
|
||||
op = copy_mov_i64(begin_op, op);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
@@ -330,23 +381,32 @@ static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
|
||||
void *func, int *cb_idx)
|
||||
{
|
||||
TCGOp *old_op;
|
||||
int func_idx;
|
||||
|
||||
/* copy all ops until the call */
|
||||
do {
|
||||
op = copy_op_nocheck(begin_op, op);
|
||||
} while (op->opc != INDEX_op_call);
|
||||
|
||||
/* fill in the op call */
|
||||
old_op = *begin_op;
|
||||
TCGOP_CALLI(op) = TCGOP_CALLI(old_op);
|
||||
TCGOP_CALLO(op) = TCGOP_CALLO(old_op);
|
||||
op->param1 = (*begin_op)->param1;
|
||||
op->param2 = (*begin_op)->param2;
|
||||
tcg_debug_assert(op->life == 0);
|
||||
if (*cb_idx == -1) {
|
||||
int i;
|
||||
|
||||
func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op);
|
||||
*cb_idx = func_idx;
|
||||
op->args[func_idx] = (uintptr_t)func;
|
||||
/*
|
||||
* Instead of working out the position of the callback in args[], just
|
||||
* look for @empty_func, since it should be a unique pointer.
|
||||
*/
|
||||
for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) {
|
||||
if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) {
|
||||
*cb_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tcg_debug_assert(i < MAX_OPC_PARAM_ARGS);
|
||||
}
|
||||
op->args[*cb_idx] = (uintptr_t)func;
|
||||
op->args[*cb_idx + 1] = (*begin_op)->args[*cb_idx + 1];
|
||||
|
||||
return op;
|
||||
}
|
||||
@@ -364,11 +424,11 @@ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
|
||||
/* copy the ld_i32, but note that we only have to copy it once */
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
if (*cb_idx == -1) {
|
||||
op = copy_op(&begin_op, op, INDEX_op_ld_i32);
|
||||
} else {
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
|
||||
memcpy(op->args, begin_op->args, sizeof(op->args));
|
||||
}
|
||||
|
||||
/* call */
|
||||
@@ -411,13 +471,16 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
|
||||
/* copy the ld_i32, but note that we only have to copy it once */
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
if (*cb_idx == -1) {
|
||||
op = copy_op(&begin_op, op, INDEX_op_ld_i32);
|
||||
} else {
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
|
||||
memcpy(op->args, begin_op->args, sizeof(op->args));
|
||||
}
|
||||
|
||||
/* extu_tl_i64 */
|
||||
op = copy_extu_tl_i64(&begin_op, op);
|
||||
|
||||
if (type == PLUGIN_GEN_CB_MEM) {
|
||||
/* call */
|
||||
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb),
|
||||
@@ -522,8 +585,7 @@ static void inject_mem_helper(TCGOp *begin_op, GArray *arr)
|
||||
* is possible that the code we generate after the instruction is
|
||||
* dead, we also add checks before generating tb_exit etc.
|
||||
*/
|
||||
static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb,
|
||||
struct qemu_plugin_insn *plugin_insn,
|
||||
static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn,
|
||||
TCGOp *begin_op)
|
||||
{
|
||||
GArray *cbs[2];
|
||||
@@ -543,7 +605,6 @@ static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb,
|
||||
rm_ops(begin_op);
|
||||
return;
|
||||
}
|
||||
ptb->mem_helper = true;
|
||||
|
||||
arr = g_array_sized_new(false, false,
|
||||
sizeof(struct qemu_plugin_dyn_cb), n_cbs);
|
||||
@@ -569,20 +630,17 @@ static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn,
|
||||
/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
|
||||
void plugin_gen_disable_mem_helpers(void)
|
||||
{
|
||||
/*
|
||||
* We could emit the clearing unconditionally and be done. However, this can
|
||||
* be wasteful if for instance plugins don't track memory accesses, or if
|
||||
* most TBs don't use helpers. Instead, emit the clearing iff the TB calls
|
||||
* helpers that might access guest memory.
|
||||
*
|
||||
* Note: we do not reset plugin_tb->mem_helper here; a TB might have several
|
||||
* exit points, and we want to emit the clearing from all of them.
|
||||
*/
|
||||
if (!tcg_ctx->plugin_tb->mem_helper) {
|
||||
TCGv_ptr ptr;
|
||||
|
||||
if (likely(tcg_ctx->plugin_insn == NULL ||
|
||||
!tcg_ctx->plugin_insn->mem_helper)) {
|
||||
return;
|
||||
}
|
||||
tcg_gen_st_ptr(tcg_constant_ptr(NULL), cpu_env,
|
||||
offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env));
|
||||
ptr = tcg_const_ptr(NULL);
|
||||
tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) -
|
||||
offsetof(ArchCPU, env));
|
||||
tcg_temp_free_ptr(ptr);
|
||||
tcg_ctx->plugin_insn->mem_helper = false;
|
||||
}
|
||||
|
||||
static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb,
|
||||
@@ -630,14 +688,14 @@ static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb,
|
||||
inject_inline_cb(cbs, begin_op, op_rw);
|
||||
}
|
||||
|
||||
static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
|
||||
static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb,
|
||||
TCGOp *begin_op, int insn_idx)
|
||||
{
|
||||
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
|
||||
inject_mem_enable_helper(ptb, insn, begin_op);
|
||||
inject_mem_enable_helper(insn, begin_op);
|
||||
}
|
||||
|
||||
static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb,
|
||||
static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb,
|
||||
TCGOp *begin_op, int insn_idx)
|
||||
{
|
||||
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
|
||||
@@ -698,7 +756,7 @@ static void pr_ops(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
|
||||
static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
|
||||
{
|
||||
TCGOp *op;
|
||||
int insn_idx = -1;
|
||||
@@ -818,7 +876,6 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
|
||||
ptb->haddr1 = db->host_addr[0];
|
||||
ptb->haddr2 = NULL;
|
||||
ptb->mem_only = mem_only;
|
||||
ptb->mem_helper = false;
|
||||
|
||||
plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#ifdef CONFIG_PLUGIN
|
||||
DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, ptr)
|
||||
DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, i32, i64, ptr)
|
||||
DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG, void, i32, ptr)
|
||||
DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG, void, i32, i32, i64, ptr)
|
||||
#endif
|
||||
|
@@ -35,16 +35,16 @@
|
||||
#define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1)
|
||||
#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
|
||||
|
||||
static inline unsigned int tb_jmp_cache_hash_page(vaddr pc)
|
||||
static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
|
||||
{
|
||||
vaddr tmp;
|
||||
target_ulong tmp;
|
||||
tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
|
||||
return (tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
|
||||
static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
|
||||
{
|
||||
vaddr tmp;
|
||||
target_ulong tmp;
|
||||
tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
|
||||
return (((tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK)
|
||||
| (tmp & TB_JMP_ADDR_MASK));
|
||||
@@ -53,7 +53,7 @@ static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
|
||||
#else
|
||||
|
||||
/* In user-mode we can get better hashing because we do not have a TLB */
|
||||
static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
|
||||
static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
|
||||
{
|
||||
return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_SIZE - 1);
|
||||
}
|
||||
@@ -61,10 +61,10 @@ static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
|
||||
static inline
|
||||
uint32_t tb_hash_func(tb_page_addr_t phys_pc, vaddr pc,
|
||||
uint32_t flags, uint64_t flags2, uint32_t cf_mask)
|
||||
uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags,
|
||||
uint32_t cf_mask, uint32_t trace_vcpu_dstate)
|
||||
{
|
||||
return qemu_xxhash8(phys_pc, pc, flags2, flags, cf_mask);
|
||||
return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -14,15 +14,52 @@
|
||||
|
||||
/*
|
||||
* Accessed in parallel; all accesses to 'tb' must be atomic.
|
||||
* For CF_PCREL, accesses to 'pc' must be protected by a
|
||||
* load_acquire/store_release to 'tb'.
|
||||
* For TARGET_TB_PCREL, accesses to 'pc' must be protected by
|
||||
* a load_acquire/store_release to 'tb'.
|
||||
*/
|
||||
struct CPUJumpCache {
|
||||
struct rcu_head rcu;
|
||||
struct {
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
#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 */
|
||||
|
1072
accel/tcg/tb-maint.c
1072
accel/tcg/tb-maint.c
File diff suppressed because it is too large
Load Diff
@@ -89,20 +89,7 @@ void icount_handle_deadline(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Distribute the budget evenly across all CPUs */
|
||||
int64_t icount_percpu_budget(int cpu_count)
|
||||
{
|
||||
int64_t limit = icount_get_limit();
|
||||
int64_t timeslice = limit / cpu_count;
|
||||
|
||||
if (timeslice == 0) {
|
||||
timeslice = limit;
|
||||
}
|
||||
|
||||
return timeslice;
|
||||
}
|
||||
|
||||
void icount_prepare_for_run(CPUState *cpu, int64_t cpu_budget)
|
||||
void icount_prepare_for_run(CPUState *cpu)
|
||||
{
|
||||
int insns_left;
|
||||
|
||||
@@ -114,13 +101,13 @@ void icount_prepare_for_run(CPUState *cpu, int64_t cpu_budget)
|
||||
g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0);
|
||||
g_assert(cpu->icount_extra == 0);
|
||||
|
||||
replay_mutex_lock();
|
||||
|
||||
cpu->icount_budget = MIN(icount_get_limit(), cpu_budget);
|
||||
cpu->icount_budget = icount_get_limit();
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_extra = cpu->icount_budget - insns_left;
|
||||
|
||||
replay_mutex_lock();
|
||||
|
||||
if (cpu->icount_budget == 0) {
|
||||
/*
|
||||
* We're called without the iothread lock, so must take it while
|
||||
|
@@ -11,8 +11,7 @@
|
||||
#define TCG_ACCEL_OPS_ICOUNT_H
|
||||
|
||||
void icount_handle_deadline(void);
|
||||
void icount_prepare_for_run(CPUState *cpu, int64_t cpu_budget);
|
||||
int64_t icount_percpu_budget(int cpu_count);
|
||||
void icount_prepare_for_run(CPUState *cpu);
|
||||
void icount_process_data(CPUState *cpu);
|
||||
|
||||
void icount_handle_interrupt(CPUState *cpu, int mask);
|
||||
|
@@ -32,7 +32,7 @@
|
||||
#include "qemu/guest-random.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "hw/boards.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
#include "tcg-accel-ops.h"
|
||||
#include "tcg-accel-ops-mttcg.h"
|
||||
|
||||
@@ -119,7 +119,7 @@ static void *mttcg_cpu_thread_fn(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
qatomic_set_mb(&cpu->exit_request, 0);
|
||||
qatomic_mb_set(&cpu->exit_request, 0);
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
|
||||
|
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
@@ -32,7 +31,7 @@
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
#include "tcg-accel-ops.h"
|
||||
#include "tcg-accel-ops-rr.h"
|
||||
#include "tcg-accel-ops-icount.h"
|
||||
@@ -72,13 +71,11 @@ static void rr_kick_next_cpu(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
do {
|
||||
cpu = qatomic_read(&rr_current_cpu);
|
||||
cpu = qatomic_mb_read(&rr_current_cpu);
|
||||
if (cpu) {
|
||||
cpu_exit(cpu);
|
||||
}
|
||||
/* Finish kicking this cpu before reading again. */
|
||||
smp_mb();
|
||||
} while (cpu != qatomic_read(&rr_current_cpu));
|
||||
} while (cpu != qatomic_mb_read(&rr_current_cpu));
|
||||
}
|
||||
|
||||
static void rr_kick_thread(void *opaque)
|
||||
@@ -142,33 +139,6 @@ static void rr_force_rcu(Notifier *notify, void *data)
|
||||
rr_kick_next_cpu();
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the number of CPUs that we will process in a single iteration of
|
||||
* the main CPU thread loop so that we can fairly distribute the instruction
|
||||
* count across CPUs.
|
||||
*
|
||||
* The CPU count is cached based on the CPU list generation ID to avoid
|
||||
* iterating the list every time.
|
||||
*/
|
||||
static int rr_cpu_count(void)
|
||||
{
|
||||
static unsigned int last_gen_id = ~0;
|
||||
static int cpu_count;
|
||||
CPUState *cpu;
|
||||
|
||||
QEMU_LOCK_GUARD(&qemu_cpu_list_lock);
|
||||
|
||||
if (cpu_list_generation_id_get() != last_gen_id) {
|
||||
cpu_count = 0;
|
||||
CPU_FOREACH(cpu) {
|
||||
++cpu_count;
|
||||
}
|
||||
last_gen_id = cpu_list_generation_id_get();
|
||||
}
|
||||
|
||||
return cpu_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -215,16 +185,11 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||
cpu->exit_request = 1;
|
||||
|
||||
while (1) {
|
||||
/* Only used for icount_enabled() */
|
||||
int64_t cpu_budget = 0;
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
replay_mutex_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (icount_enabled()) {
|
||||
int cpu_count = rr_cpu_count();
|
||||
|
||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||
icount_account_warp_timer();
|
||||
/*
|
||||
@@ -232,8 +197,6 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||
* waking up the I/O thread and waiting for completion.
|
||||
*/
|
||||
icount_handle_deadline();
|
||||
|
||||
cpu_budget = icount_percpu_budget(cpu_count);
|
||||
}
|
||||
|
||||
replay_mutex_unlock();
|
||||
@@ -243,9 +206,8 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||
}
|
||||
|
||||
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
|
||||
/* Store rr_current_cpu before evaluating cpu_can_run(). */
|
||||
qatomic_set_mb(&rr_current_cpu, cpu);
|
||||
|
||||
qatomic_mb_set(&rr_current_cpu, cpu);
|
||||
current_cpu = cpu;
|
||||
|
||||
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
|
||||
@@ -256,7 +218,7 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
if (icount_enabled()) {
|
||||
icount_prepare_for_run(cpu, cpu_budget);
|
||||
icount_prepare_for_run(cpu);
|
||||
}
|
||||
r = tcg_cpus_exec(cpu);
|
||||
if (icount_enabled()) {
|
||||
@@ -283,11 +245,11 @@ static void *rr_cpu_thread_fn(void *arg)
|
||||
cpu = CPU_NEXT(cpu);
|
||||
} /* while (cpu && !cpu->exit_request).. */
|
||||
|
||||
/* Does not need a memory barrier because a spurious wakeup is okay. */
|
||||
/* Does not need qatomic_mb_set because a spurious wakeup is okay. */
|
||||
qatomic_set(&rr_current_cpu, NULL);
|
||||
|
||||
if (cpu && cpu->exit_request) {
|
||||
qatomic_set_mb(&cpu->exit_request, 0);
|
||||
qatomic_mb_set(&cpu->exit_request, 0);
|
||||
}
|
||||
|
||||
if (icount_enabled() && all_cpu_threads_idle()) {
|
||||
|
@@ -31,7 +31,6 @@
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/gdbstub.h"
|
||||
@@ -45,21 +44,10 @@
|
||||
|
||||
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel)
|
||||
{
|
||||
uint32_t cflags;
|
||||
|
||||
/*
|
||||
* Include the cluster number in the hash we use to look up TBs.
|
||||
* This is important because a TB that is valid for one cluster at
|
||||
* a given physical address and set of CPU flags is not necessarily
|
||||
* valid for another:
|
||||
* the two clusters may have different views of physical memory, or
|
||||
* may have different CPU features (eg FPU present or absent).
|
||||
*/
|
||||
cflags = cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||
|
||||
uint32_t cflags = cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||
cflags |= parallel ? CF_PARALLEL : 0;
|
||||
cflags |= icount_enabled() ? CF_USE_ICOUNT : 0;
|
||||
cpu->tcg_cflags |= cflags;
|
||||
cpu->tcg_cflags = cflags;
|
||||
}
|
||||
|
||||
void tcg_cpus_destroy(CPUState *cpu)
|
||||
@@ -70,10 +58,20 @@ void tcg_cpus_destroy(CPUState *cpu)
|
||||
int tcg_cpus_exec(CPUState *cpu)
|
||||
{
|
||||
int ret;
|
||||
#ifdef CONFIG_PROFILER
|
||||
int64_t ti;
|
||||
#endif
|
||||
assert(tcg_enabled());
|
||||
#ifdef CONFIG_PROFILER
|
||||
ti = profile_getclock();
|
||||
#endif
|
||||
cpu_exec_start(cpu);
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
#ifdef CONFIG_PROFILER
|
||||
qatomic_set(&tcg_ctx->prof.cpu_exec_time,
|
||||
tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -118,7 +116,7 @@ static inline int xlat_gdb_type(CPUState *cpu, int gdbtype)
|
||||
return cputype;
|
||||
}
|
||||
|
||||
static int tcg_insert_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len)
|
||||
static int tcg_insert_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
CPUState *cpu;
|
||||
int err = 0;
|
||||
@@ -149,7 +147,7 @@ static int tcg_insert_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len)
|
||||
}
|
||||
}
|
||||
|
||||
static int tcg_remove_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len)
|
||||
static int tcg_remove_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len)
|
||||
{
|
||||
CPUState *cpu;
|
||||
int err = 0;
|
||||
|
@@ -25,14 +25,12 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "exec/replay-core.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg/oversized-guest.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "qemu/units.h"
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@@ -44,7 +42,6 @@ struct TCGState {
|
||||
AccelState parent_obj;
|
||||
|
||||
bool mttcg_enabled;
|
||||
bool one_insn_per_tb;
|
||||
int splitwx_enabled;
|
||||
unsigned long tb_size;
|
||||
};
|
||||
@@ -64,23 +61,37 @@ DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE,
|
||||
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
|
||||
*
|
||||
* Once a guest architecture has been converted to the new primitives
|
||||
* there is one remaining limitation to check:
|
||||
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
|
||||
* there are two remaining limitations to check.
|
||||
*
|
||||
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
|
||||
* - The host must have a stronger memory order than the guest
|
||||
*
|
||||
* It may be possible in future to support strong guests on weak hosts
|
||||
* but that will require tagging all load/stores in a guest with their
|
||||
* implicit memory order requirements which would likely slow things
|
||||
* down a lot.
|
||||
*/
|
||||
|
||||
static bool check_tcg_memory_orders_compatible(void)
|
||||
{
|
||||
#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO)
|
||||
return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool default_mttcg_enabled(void)
|
||||
{
|
||||
if (icount_enabled() || TCG_OVERSIZED_GUEST) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
#ifdef TARGET_SUPPORTS_MTTCG
|
||||
# ifndef TCG_GUEST_DEFAULT_MO
|
||||
# error "TARGET_SUPPORTS_MTTCG without TCG_GUEST_DEFAULT_MO"
|
||||
# endif
|
||||
return true;
|
||||
return check_tcg_memory_orders_compatible();
|
||||
#else
|
||||
return false;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_accel_instance_init(Object *obj)
|
||||
@@ -98,7 +109,6 @@ static void tcg_accel_instance_init(Object *obj)
|
||||
}
|
||||
|
||||
bool mttcg_enabled;
|
||||
bool one_insn_per_tb;
|
||||
|
||||
static int tcg_init_machine(MachineState *ms)
|
||||
{
|
||||
@@ -148,6 +158,11 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp)
|
||||
warn_report("Guest not yet converted to MTTCG - "
|
||||
"you may get unexpected results");
|
||||
#endif
|
||||
if (!check_tcg_memory_orders_compatible()) {
|
||||
warn_report("Guest expects a stronger memory ordering "
|
||||
"than the host provides");
|
||||
error_printf("This may cause strange/hard to debug errors\n");
|
||||
}
|
||||
s->mttcg_enabled = true;
|
||||
}
|
||||
} else if (strcmp(value, "single") == 0) {
|
||||
@@ -193,20 +208,6 @@ static void tcg_set_splitwx(Object *obj, bool value, Error **errp)
|
||||
s->splitwx_enabled = value;
|
||||
}
|
||||
|
||||
static bool tcg_get_one_insn_per_tb(Object *obj, Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
return s->one_insn_per_tb;
|
||||
}
|
||||
|
||||
static void tcg_set_one_insn_per_tb(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
s->one_insn_per_tb = value;
|
||||
/* Set the global also: this changes the behaviour */
|
||||
qatomic_set(&one_insn_per_tb, value);
|
||||
}
|
||||
|
||||
static int tcg_gdbstub_supported_sstep_flags(void)
|
||||
{
|
||||
/*
|
||||
@@ -244,12 +245,6 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
tcg_get_splitwx, tcg_set_splitwx);
|
||||
object_class_property_set_description(oc, "split-wx",
|
||||
"Map jit pages into separate RW and RX regions");
|
||||
|
||||
object_class_property_add_bool(oc, "one-insn-per-tb",
|
||||
tcg_get_one_insn_per_tb,
|
||||
tcg_set_one_insn_per_tb);
|
||||
object_class_property_set_description(oc, "one-insn-per-tb",
|
||||
"Only put one guest insn in each translation block");
|
||||
}
|
||||
|
||||
static const TypeInfo tcg_accel_type = {
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "tcg/tcg-gvec-desc.h"
|
||||
|
||||
|
||||
@@ -550,17 +550,6 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_andcs)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) & ~b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
|
@@ -24,17 +24,13 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/log.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
#define HELPER_H "accel/tcg/tcg-runtime.h"
|
||||
#include "exec/helper-info.c.inc"
|
||||
#undef HELPER_H
|
||||
|
||||
/* 32-bit helpers */
|
||||
|
||||
int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2)
|
||||
|
@@ -39,63 +39,51 @@ DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
|
||||
DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr)
|
||||
#endif /* IN_HELPER_PROTO */
|
||||
|
||||
DEF_HELPER_FLAGS_3(ld_i128, TCG_CALL_NO_WG, i128, env, i64, i32)
|
||||
DEF_HELPER_FLAGS_4(st_i128, TCG_CALL_NO_WG, void, env, i64, i128, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG,
|
||||
i32, env, i64, i32, i32, i32)
|
||||
i32, env, tl, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgw_be, TCG_CALL_NO_WG,
|
||||
i32, env, i64, i32, i32, i32)
|
||||
i32, env, tl, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgw_le, TCG_CALL_NO_WG,
|
||||
i32, env, i64, i32, i32, i32)
|
||||
i32, env, tl, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgl_be, TCG_CALL_NO_WG,
|
||||
i32, env, i64, i32, i32, i32)
|
||||
i32, env, tl, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgl_le, TCG_CALL_NO_WG,
|
||||
i32, env, i64, i32, i32, i32)
|
||||
i32, env, tl, i32, i32, i32)
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgq_be, TCG_CALL_NO_WG,
|
||||
i64, env, i64, i64, i64, i32)
|
||||
i64, env, tl, i64, i64, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgq_le, TCG_CALL_NO_WG,
|
||||
i64, env, i64, i64, i64, i32)
|
||||
i64, env, tl, i64, i64, i32)
|
||||
#endif
|
||||
#ifdef CONFIG_CMPXCHG128
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG,
|
||||
i128, env, i64, i128, i128, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
|
||||
i128, env, i64, i128, i128, i32)
|
||||
#endif
|
||||
|
||||
DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG,
|
||||
i128, env, i64, i128, i128, i32)
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#define GEN_ATOMIC_HELPERS(NAME) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_le), \
|
||||
TCG_CALL_NO_WG, i64, env, i64, i64, i32) \
|
||||
TCG_CALL_NO_WG, i64, env, tl, i64, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_be), \
|
||||
TCG_CALL_NO_WG, i64, env, i64, i64, i32)
|
||||
TCG_CALL_NO_WG, i64, env, tl, i64, i32)
|
||||
#else
|
||||
#define GEN_ATOMIC_HELPERS(NAME) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32) \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
|
||||
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \
|
||||
TCG_CALL_NO_WG, i32, env, i64, i32, i32)
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
#endif /* CONFIG_ATOMIC64 */
|
||||
|
||||
GEN_ATOMIC_HELPERS(fetch_add)
|
||||
@@ -218,7 +206,6 @@ DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_andcs, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_ors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||
|
||||
|
@@ -6,9 +6,5 @@ exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x"
|
||||
|
||||
# cputlb.c
|
||||
memory_notdirty_write_access(uint64_t vaddr, uint64_t ram_addr, unsigned size) "0x%" PRIx64 " ram_addr 0x%" PRIx64 " size %u"
|
||||
memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64
|
||||
|
||||
# translate-all.c
|
||||
translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,116 +8,30 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/gen-icount.h"
|
||||
#include "exec/log.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/translate-all.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
#include "tcg/tcg-op-common.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
static void gen_io_start(void)
|
||||
/* Pairs with tcg_clear_temp_count.
|
||||
To be called by #TranslatorOps.{translate_insn,tb_stop} if
|
||||
(1) the target is sufficiently clean to support reporting,
|
||||
(2) as and when all temporaries are known to be consumed.
|
||||
For most targets, (2) is at the end of translate_insn. */
|
||||
void translator_loop_temp_check(DisasContextBase *db)
|
||||
{
|
||||
tcg_gen_st_i32(tcg_constant_i32(1), cpu_env,
|
||||
offsetof(ArchCPU, parent_obj.can_do_io) -
|
||||
offsetof(ArchCPU, env));
|
||||
}
|
||||
|
||||
bool translator_io_start(DisasContextBase *db)
|
||||
{
|
||||
uint32_t cflags = tb_cflags(db->tb);
|
||||
|
||||
if (!(cflags & CF_USE_ICOUNT)) {
|
||||
return false;
|
||||
}
|
||||
if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
|
||||
/* Already started in translator_loop. */
|
||||
return true;
|
||||
}
|
||||
|
||||
gen_io_start();
|
||||
|
||||
/*
|
||||
* Ensure that this instruction will be the last in the TB.
|
||||
* The target may override this to something more forceful.
|
||||
*/
|
||||
if (db->is_jmp == DISAS_NEXT) {
|
||||
db->is_jmp = DISAS_TOO_MANY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static TCGOp *gen_tb_start(uint32_t cflags)
|
||||
{
|
||||
TCGv_i32 count = tcg_temp_new_i32();
|
||||
TCGOp *icount_start_insn = NULL;
|
||||
|
||||
tcg_gen_ld_i32(count, cpu_env,
|
||||
offsetof(ArchCPU, neg.icount_decr.u32) -
|
||||
offsetof(ArchCPU, env));
|
||||
|
||||
if (cflags & CF_USE_ICOUNT) {
|
||||
/*
|
||||
* We emit a sub with a dummy immediate argument. Keep the insn index
|
||||
* of the sub so that we later (when we know the actual insn count)
|
||||
* can update the argument with the actual insn count.
|
||||
*/
|
||||
tcg_gen_sub_i32(count, count, tcg_constant_i32(0));
|
||||
icount_start_insn = tcg_last_op();
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit the check against icount_decr.u32 to see if we should exit
|
||||
* unless we suppress the check with CF_NOIRQ. If we are using
|
||||
* icount and have suppressed interruption the higher level code
|
||||
* should have ensured we don't run more instructions than the
|
||||
* budget.
|
||||
*/
|
||||
if (cflags & CF_NOIRQ) {
|
||||
tcg_ctx->exitreq_label = NULL;
|
||||
} else {
|
||||
tcg_ctx->exitreq_label = gen_new_label();
|
||||
tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label);
|
||||
}
|
||||
|
||||
if (cflags & CF_USE_ICOUNT) {
|
||||
tcg_gen_st16_i32(count, cpu_env,
|
||||
offsetof(ArchCPU, neg.icount_decr.u16.low) -
|
||||
offsetof(ArchCPU, env));
|
||||
/*
|
||||
* cpu->can_do_io is cleared automatically here at the beginning of
|
||||
* each translation block. The cost is minimal and only paid for
|
||||
* -icount, plus it would be very easy to forget doing it in the
|
||||
* translator. Doing it here means we don't need a gen_io_end() to
|
||||
* go with gen_io_start().
|
||||
*/
|
||||
tcg_gen_st_i32(tcg_constant_i32(0), cpu_env,
|
||||
offsetof(ArchCPU, parent_obj.can_do_io) -
|
||||
offsetof(ArchCPU, env));
|
||||
}
|
||||
|
||||
return icount_start_insn;
|
||||
}
|
||||
|
||||
static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags,
|
||||
TCGOp *icount_start_insn, int num_insns)
|
||||
{
|
||||
if (cflags & CF_USE_ICOUNT) {
|
||||
/*
|
||||
* Update the num_insn immediate parameter now that we know
|
||||
* the actual insn count.
|
||||
*/
|
||||
tcg_set_insn_param(icount_start_insn, 2,
|
||||
tcgv_i32_arg(tcg_constant_i32(num_insns)));
|
||||
}
|
||||
|
||||
if (tcg_ctx->exitreq_label) {
|
||||
gen_set_label(tcg_ctx->exitreq_label);
|
||||
tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED);
|
||||
if (tcg_check_temp_count()) {
|
||||
qemu_log("warning: TCG temporary leaks before "
|
||||
TARGET_FMT_lx "\n", db->pc_next);
|
||||
}
|
||||
}
|
||||
|
||||
bool translator_use_goto_tb(DisasContextBase *db, vaddr dest)
|
||||
bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
|
||||
{
|
||||
/* Suppress goto_tb if requested. */
|
||||
if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
|
||||
@@ -128,12 +42,11 @@ bool translator_use_goto_tb(DisasContextBase *db, vaddr dest)
|
||||
return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
|
||||
}
|
||||
|
||||
void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
vaddr pc, void *host_pc, const TranslatorOps *ops,
|
||||
DisasContextBase *db)
|
||||
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);
|
||||
TCGOp *icount_start_insn;
|
||||
bool plugin_enabled;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
@@ -142,7 +55,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
db->pc_next = pc;
|
||||
db->is_jmp = DISAS_NEXT;
|
||||
db->num_insns = 0;
|
||||
db->max_insns = *max_insns;
|
||||
db->max_insns = max_insns;
|
||||
db->singlestep_enabled = cflags & CF_SINGLE_STEP;
|
||||
db->host_addr[0] = host_pc;
|
||||
db->host_addr[1] = NULL;
|
||||
@@ -154,15 +67,18 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
ops->init_disas_context(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Reset the temp count so that we can identify leaks */
|
||||
tcg_clear_temp_count();
|
||||
|
||||
/* Start translating. */
|
||||
icount_start_insn = gen_tb_start(cflags);
|
||||
gen_tb_start(db->tb);
|
||||
ops->tb_start(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
|
||||
|
||||
while (true) {
|
||||
*max_insns = ++db->num_insns;
|
||||
db->num_insns++;
|
||||
ops->insn_start(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
@@ -184,24 +100,19 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
ops->translate_insn(db, cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't instrument after instructions that change control
|
||||
* flow although this only really affects post-load operations.
|
||||
*
|
||||
* Calling plugin_gen_insn_end() before we possibly stop translation
|
||||
* is important. Even if this ends up as dead code, plugin generation
|
||||
* needs to see a matching plugin_gen_insn_{start,end}() pair in order
|
||||
* to accurately track instrumented helpers that might access memory.
|
||||
*/
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_insn_end();
|
||||
}
|
||||
|
||||
/* Stop translation if translate_insn so indicated. */
|
||||
if (db->is_jmp != DISAS_NEXT) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't instrument after instructions that change control
|
||||
* flow although this only really affects post-load operations.
|
||||
*/
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_insn_end();
|
||||
}
|
||||
|
||||
/* Stop translation if the output buffer is full,
|
||||
or we have executed all of the allowed instructions. */
|
||||
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
|
||||
@@ -212,7 +123,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
|
||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(tb, cflags, icount_start_insn, db->num_insns);
|
||||
gen_tb_end(db->tb, db->num_insns);
|
||||
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_tb_end(cpu);
|
||||
@@ -222,6 +133,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
tb->size = db->pc_next - db->pc_first;
|
||||
tb->icount = db->num_insns;
|
||||
|
||||
#ifdef DEBUG_DISAS
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
|
||||
&& qemu_log_in_addr_range(db->pc_first)) {
|
||||
FILE *logfile = qemu_log_trylock();
|
||||
@@ -232,13 +144,14 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
qemu_log_unlock(logfile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *translator_access(CPUArchState *env, DisasContextBase *db,
|
||||
vaddr pc, size_t len)
|
||||
target_ulong pc, size_t len)
|
||||
{
|
||||
void *host;
|
||||
vaddr base, end;
|
||||
target_ulong base, end;
|
||||
TranslationBlock *tb;
|
||||
|
||||
tb = db->tb;
|
||||
@@ -258,16 +171,8 @@ static void *translator_access(CPUArchState *env, DisasContextBase *db,
|
||||
if (host == NULL) {
|
||||
tb_page_addr_t phys_page =
|
||||
get_page_addr_code_hostp(env, base, &db->host_addr[1]);
|
||||
|
||||
/*
|
||||
* If the second page is MMIO, treat as if the first page
|
||||
* was MMIO as well, so that we do not cache the TB.
|
||||
*/
|
||||
if (unlikely(phys_page == -1)) {
|
||||
tb_set_page_addr0(tb, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -285,27 +190,6 @@ static void *translator_access(CPUArchState *env, DisasContextBase *db,
|
||||
return host + (pc - base);
|
||||
}
|
||||
|
||||
static void plugin_insn_append(abi_ptr pc, const void *from, size_t size)
|
||||
{
|
||||
#ifdef CONFIG_PLUGIN
|
||||
struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn;
|
||||
abi_ptr off;
|
||||
|
||||
if (insn == NULL) {
|
||||
return;
|
||||
}
|
||||
off = pc - insn->vaddr;
|
||||
if (off < insn->data->len) {
|
||||
g_byte_array_set_size(insn->data, off);
|
||||
} else if (off > insn->data->len) {
|
||||
/* we have an unexpected gap */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
insn->data = g_byte_array_append(insn->data, from, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
|
||||
{
|
||||
uint8_t ret;
|
||||
@@ -364,8 +248,3 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
|
||||
plugin_insn_append(pc, &plug, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void translator_fake_ldb(uint8_t insn8, abi_ptr pc)
|
||||
{
|
||||
plugin_insn_append(pc, &insn8, sizeof(insn8));
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "exec/replay-core.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
bool enable_cpu_pm = false;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/xen/xen_native.h"
|
||||
#include "hw/xen/xen-legacy-backend.h"
|
||||
#include "hw/xen/xen_pt.h"
|
||||
#include "chardev/char.h"
|
||||
@@ -24,18 +23,99 @@
|
||||
#include "migration/global_state.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
//#define DEBUG_XEN
|
||||
|
||||
#ifdef DEBUG_XEN
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
bool xen_allowed;
|
||||
|
||||
xc_interface *xen_xc;
|
||||
xenforeignmemory_handle *xen_fmem;
|
||||
xendevicemodel_handle *xen_dmod;
|
||||
|
||||
static void xenstore_record_dm_state(const char *state)
|
||||
static int store_dev_info(int domid, Chardev *cs, const char *string)
|
||||
{
|
||||
struct xs_handle *xs = NULL;
|
||||
char *path = NULL;
|
||||
char *newpath = NULL;
|
||||
char *pts = NULL;
|
||||
int ret = -1;
|
||||
|
||||
/* Only continue if we're talking to a pty. */
|
||||
if (!CHARDEV_IS_PTY(cs)) {
|
||||
return 0;
|
||||
}
|
||||
pts = cs->filename + 4;
|
||||
|
||||
/* We now have everything we need to set the xenstore entry. */
|
||||
xs = xs_open(0);
|
||||
if (xs == NULL) {
|
||||
fprintf(stderr, "Could not contact XenStore\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
path = xs_get_domain_path(xs, domid);
|
||||
if (path == NULL) {
|
||||
fprintf(stderr, "xs_get_domain_path() error\n");
|
||||
goto out;
|
||||
}
|
||||
newpath = realloc(path, (strlen(path) + strlen(string) +
|
||||
strlen("/tty") + 1));
|
||||
if (newpath == NULL) {
|
||||
fprintf(stderr, "realloc error\n");
|
||||
goto out;
|
||||
}
|
||||
path = newpath;
|
||||
|
||||
strcat(path, string);
|
||||
strcat(path, "/tty");
|
||||
if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) {
|
||||
fprintf(stderr, "xs_write for '%s' fail", string);
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
free(path);
|
||||
xs_close(xs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xenstore_store_pv_console_info(int i, Chardev *chr)
|
||||
{
|
||||
if (i == 0) {
|
||||
store_dev_info(xen_domid, chr, "/console");
|
||||
} else {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "/device/console/%d", i);
|
||||
store_dev_info(xen_domid, chr, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void xenstore_record_dm_state(struct xs_handle *xs, const char *state)
|
||||
{
|
||||
char path[50];
|
||||
|
||||
if (xs == NULL) {
|
||||
error_report("xenstore connection not initialized");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof (path), "device-model/%u/state", xen_domid);
|
||||
if (!qemu_xen_xs_write(xenstore, XBT_NULL, path, state, strlen(state))) {
|
||||
/*
|
||||
* This call may fail when running restricted so don't make it fatal in
|
||||
* that case. Toolstacks should instead use QMP to listen for state changes.
|
||||
*/
|
||||
if (!xs_write(xs, XBT_NULL, path, state, strlen(state)) &&
|
||||
!xen_domid_restrict) {
|
||||
error_report("error recording dm state");
|
||||
exit(1);
|
||||
}
|
||||
@@ -47,7 +127,7 @@ static void xen_change_state_handler(void *opaque, bool running,
|
||||
{
|
||||
if (running) {
|
||||
/* record state running */
|
||||
xenstore_record_dm_state("running");
|
||||
xenstore_record_dm_state(xenstore, "running");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,21 +176,11 @@ static int xen_init(MachineState *ms)
|
||||
xc_interface_close(xen_xc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The XenStore write would fail when running restricted so don't attempt
|
||||
* it in that case. Toolstacks should instead use QMP to listen for state
|
||||
* changes.
|
||||
*/
|
||||
if (!xen_domid_restrict) {
|
||||
qemu_add_vm_change_state_handler(xen_change_state_handler, NULL);
|
||||
}
|
||||
qemu_add_vm_change_state_handler(xen_change_state_handler, NULL);
|
||||
/*
|
||||
* opt out of system RAM being allocated by generic code
|
||||
*/
|
||||
mc->default_ram_id = NULL;
|
||||
|
||||
xen_mode = XEN_ATTACH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -222,7 +222,11 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
|
||||
return -1;
|
||||
}
|
||||
|
||||
pfds = g_new0(struct pollfd, count);
|
||||
pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds));
|
||||
if (!pfds) {
|
||||
dolog ("Could not initialize poll mode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = snd_pcm_poll_descriptors (handle, pfds, count);
|
||||
if (err < 0) {
|
||||
@@ -445,7 +449,7 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
int err;
|
||||
unsigned int freq, nchannels;
|
||||
const char *pcm_name = apdo->dev ?: "default";
|
||||
const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
|
||||
snd_pcm_uframes_t obt_buffer_size;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
snd_pcm_format_t obtfmt;
|
||||
@@ -913,23 +917,28 @@ static void *alsa_audio_init(Audiodev *dev)
|
||||
alsa_init_per_direction(aopts->in);
|
||||
alsa_init_per_direction(aopts->out);
|
||||
|
||||
/* don't set has_* so alsa_open can identify it wasn't set by the user */
|
||||
/*
|
||||
* need to define them, as otherwise alsa produces no sound
|
||||
* doesn't set has_* so alsa_open can identify it wasn't set by the user
|
||||
*/
|
||||
if (!dev->u.alsa.out->has_period_length) {
|
||||
/* 256 frames assuming 44100Hz */
|
||||
dev->u.alsa.out->period_length = 5805;
|
||||
/* 1024 frames assuming 44100Hz */
|
||||
dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
|
||||
}
|
||||
if (!dev->u.alsa.out->has_buffer_length) {
|
||||
/* 4096 frames assuming 44100Hz */
|
||||
dev->u.alsa.out->buffer_length = 92880;
|
||||
dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
|
||||
}
|
||||
|
||||
/*
|
||||
* OptsVisitor sets unspecified optional fields to zero, but do not depend
|
||||
* on it...
|
||||
*/
|
||||
if (!dev->u.alsa.in->has_period_length) {
|
||||
/* 256 frames assuming 44100Hz */
|
||||
dev->u.alsa.in->period_length = 5805;
|
||||
dev->u.alsa.in->period_length = 0;
|
||||
}
|
||||
if (!dev->u.alsa.in->has_buffer_length) {
|
||||
/* 4096 frames assuming 44100Hz */
|
||||
dev->u.alsa.in->buffer_length = 92880;
|
||||
dev->u.alsa.in->buffer_length = 0;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* HMP commands related to audio backends
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* 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 "audio/audio.h"
|
||||
#include "monitor/hmp.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
||||
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
|
||||
|
||||
void hmp_info_capture(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
int i;
|
||||
CaptureState *s;
|
||||
|
||||
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
||||
monitor_printf(mon, "[%d]: ", i);
|
||||
s->ops.info (s->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
void hmp_stopcapture(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
int i;
|
||||
int n = qdict_get_int(qdict, "n");
|
||||
CaptureState *s;
|
||||
|
||||
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
|
||||
if (i == n) {
|
||||
s->ops.destroy (s->opaque);
|
||||
QLIST_REMOVE (s, entries);
|
||||
g_free (s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hmp_wavcapture(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *path = qdict_get_str(qdict, "path");
|
||||
int freq = qdict_get_try_int(qdict, "freq", 44100);
|
||||
int bits = qdict_get_try_int(qdict, "bits", 16);
|
||||
int nchannels = qdict_get_try_int(qdict, "nchannels", 2);
|
||||
const char *audiodev = qdict_get_str(qdict, "audiodev");
|
||||
CaptureState *s;
|
||||
AudioState *as = audio_state_by_name(audiodev);
|
||||
|
||||
if (!as) {
|
||||
monitor_printf(mon, "Audiodev '%s' not found\n", audiodev);
|
||||
return;
|
||||
}
|
||||
|
||||
s = g_malloc0 (sizeof (*s));
|
||||
|
||||
if (wav_start_capture(as, s, path, freq, bits, nchannels)) {
|
||||
monitor_printf(mon, "Failed to add wave capture\n");
|
||||
g_free (s);
|
||||
return;
|
||||
}
|
||||
QLIST_INSERT_HEAD (&capture_head, s, entries);
|
||||
}
|
431
audio/audio.c
431
audio/audio.c
@@ -28,12 +28,9 @@
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-audio.h"
|
||||
#include "qapi/qapi-commands-audio.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/help_option.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
@@ -149,6 +146,26 @@ static inline int audio_bits_to_index (int bits)
|
||||
}
|
||||
}
|
||||
|
||||
void *audio_calloc (const char *funcname, int nmemb, size_t size)
|
||||
{
|
||||
int cond;
|
||||
size_t len;
|
||||
|
||||
len = nmemb * size;
|
||||
cond = !nmemb || !size;
|
||||
cond |= nmemb < 0;
|
||||
cond |= len < size;
|
||||
|
||||
if (audio_bug ("audio_calloc", cond)) {
|
||||
AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
|
||||
funcname);
|
||||
AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_malloc0 (len);
|
||||
}
|
||||
|
||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
|
||||
{
|
||||
if (cap) {
|
||||
@@ -381,6 +398,13 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
|
||||
/*
|
||||
* Capture
|
||||
*/
|
||||
static void noop_conv (struct st_sample *dst, const void *src, int samples)
|
||||
{
|
||||
(void) src;
|
||||
(void) dst;
|
||||
(void) samples;
|
||||
}
|
||||
|
||||
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
|
||||
struct audsettings *as)
|
||||
{
|
||||
@@ -478,8 +502,15 @@ static int audio_attach_capture (HWVoiceOut *hw)
|
||||
sw->info = hw->info;
|
||||
sw->empty = 1;
|
||||
sw->active = hw->enabled;
|
||||
sw->conv = noop_conv;
|
||||
sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
|
||||
sw->vol = nominal_volume;
|
||||
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
|
||||
if (!sw->rate) {
|
||||
dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
|
||||
g_free (sw);
|
||||
return -1;
|
||||
}
|
||||
QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
|
||||
QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);
|
||||
#ifdef DEBUG_CAPTURE
|
||||
@@ -514,8 +545,8 @@ static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
|
||||
static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
|
||||
{
|
||||
size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
|
||||
if (audio_bug(__func__, live > hw->conv_buf.size)) {
|
||||
dolog("live=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size);
|
||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
@@ -524,13 +555,13 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
|
||||
static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
|
||||
{
|
||||
size_t conv = 0;
|
||||
STSampleBuffer *conv_buf = &hw->conv_buf;
|
||||
STSampleBuffer *conv_buf = hw->conv_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->conv(conv_buf->buffer + conv_buf->pos, src, proc);
|
||||
hw->conv(conv_buf->samples + conv_buf->pos, src, proc);
|
||||
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
|
||||
samples -= proc;
|
||||
conv += proc;
|
||||
@@ -542,65 +573,56 @@ static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
|
||||
/*
|
||||
* Soft voice (capture)
|
||||
*/
|
||||
static void audio_pcm_sw_resample_in(SWVoiceIn *sw,
|
||||
size_t frames_in_max, size_t frames_out_max,
|
||||
size_t *total_in, size_t *total_out)
|
||||
static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||
{
|
||||
HWVoiceIn *hw = sw->hw;
|
||||
struct st_sample *src, *dst;
|
||||
size_t live, rpos, frames_in, frames_out;
|
||||
|
||||
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
rpos = audio_ring_posb(hw->conv_buf.pos, live, hw->conv_buf.size);
|
||||
|
||||
/* resample conv_buf from rpos to end of buffer */
|
||||
src = hw->conv_buf.buffer + rpos;
|
||||
frames_in = MIN(frames_in_max, hw->conv_buf.size - rpos);
|
||||
dst = sw->resample_buf.buffer;
|
||||
frames_out = frames_out_max;
|
||||
st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);
|
||||
rpos += frames_in;
|
||||
*total_in = frames_in;
|
||||
*total_out = frames_out;
|
||||
|
||||
/* resample conv_buf from start of buffer if there are input frames left */
|
||||
if (frames_in_max - frames_in && rpos == hw->conv_buf.size) {
|
||||
src = hw->conv_buf.buffer;
|
||||
frames_in = frames_in_max - frames_in;
|
||||
dst += frames_out;
|
||||
frames_out = frames_out_max - frames_out;
|
||||
st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);
|
||||
*total_in += frames_in;
|
||||
*total_out += frames_out;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t buf_len)
|
||||
{
|
||||
HWVoiceIn *hw = sw->hw;
|
||||
size_t live, frames_out_max, total_in, total_out;
|
||||
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
|
||||
struct st_sample *src, *dst = sw->buf;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
frames_out_max = MIN(buf_len / sw->info.bytes_per_frame,
|
||||
sw->resample_buf.size);
|
||||
rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
|
||||
|
||||
audio_pcm_sw_resample_in(sw, live, frames_out_max, &total_in, &total_out);
|
||||
samples = size / sw->info.bytes_per_frame;
|
||||
|
||||
swlim = (live * sw->ratio) >> 32;
|
||||
swlim = MIN (swlim, samples);
|
||||
|
||||
while (swlim) {
|
||||
src = hw->conv_buf->samples + rpos;
|
||||
if (hw->conv_buf->pos > rpos) {
|
||||
isamp = hw->conv_buf->pos - rpos;
|
||||
} else {
|
||||
isamp = hw->conv_buf->size - rpos;
|
||||
}
|
||||
|
||||
if (!isamp) {
|
||||
break;
|
||||
}
|
||||
osamp = swlim;
|
||||
|
||||
st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
|
||||
swlim -= osamp;
|
||||
rpos = (rpos + isamp) % hw->conv_buf->size;
|
||||
dst += osamp;
|
||||
ret += osamp;
|
||||
total += isamp;
|
||||
}
|
||||
|
||||
if (!hw->pcm_ops->volume_in) {
|
||||
mixeng_volume(sw->resample_buf.buffer, total_out, &sw->vol);
|
||||
mixeng_volume (sw->buf, ret, &sw->vol);
|
||||
}
|
||||
sw->clip(buf, sw->resample_buf.buffer, total_out);
|
||||
|
||||
sw->total_hw_samples_acquired += total_in;
|
||||
return total_out * sw->info.bytes_per_frame;
|
||||
sw->clip (buf, sw->buf, ret);
|
||||
sw->total_hw_samples_acquired += total;
|
||||
return ret * sw->info.bytes_per_frame;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -636,8 +658,8 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
|
||||
if (nb_live1) {
|
||||
size_t live = smin;
|
||||
|
||||
if (audio_bug(__func__, live > hw->mix_buf.size)) {
|
||||
dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
|
||||
if (audio_bug(__func__, live > hw->mix_buf->size)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
@@ -654,17 +676,17 @@ static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)
|
||||
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;
|
||||
size_t pos = hw->mix_buf->pos;
|
||||
|
||||
while (len) {
|
||||
st_sample *src = hw->mix_buf.buffer + pos;
|
||||
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_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;
|
||||
pos = (pos + samples_to_clip) % hw->mix_buf->size;
|
||||
len -= samples_to_clip;
|
||||
clipped += samples_to_clip;
|
||||
}
|
||||
@@ -673,113 +695,84 @@ static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
|
||||
/*
|
||||
* Soft voice (playback)
|
||||
*/
|
||||
static void audio_pcm_sw_resample_out(SWVoiceOut *sw,
|
||||
size_t frames_in_max, size_t frames_out_max,
|
||||
size_t *total_in, size_t *total_out)
|
||||
static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
{
|
||||
HWVoiceOut *hw = sw->hw;
|
||||
struct st_sample *src, *dst;
|
||||
size_t live, wpos, frames_in, frames_out;
|
||||
size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, blck;
|
||||
size_t hw_free;
|
||||
size_t ret = 0, pos = 0, total = 0;
|
||||
|
||||
live = sw->total_hw_samples_mixed;
|
||||
wpos = (hw->mix_buf.pos + live) % hw->mix_buf.size;
|
||||
|
||||
/* write to mix_buf from wpos to end of buffer */
|
||||
src = sw->resample_buf.buffer;
|
||||
frames_in = frames_in_max;
|
||||
dst = hw->mix_buf.buffer + wpos;
|
||||
frames_out = MIN(frames_out_max, hw->mix_buf.size - wpos);
|
||||
st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);
|
||||
wpos += frames_out;
|
||||
*total_in = frames_in;
|
||||
*total_out = frames_out;
|
||||
|
||||
/* write to mix_buf from start of buffer if there are input frames left */
|
||||
if (frames_in_max - frames_in > 0 && wpos == hw->mix_buf.size) {
|
||||
src += frames_in;
|
||||
frames_in = frames_in_max - frames_in;
|
||||
dst = hw->mix_buf.buffer;
|
||||
frames_out = frames_out_max - frames_out;
|
||||
st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);
|
||||
*total_in += frames_in;
|
||||
*total_out += frames_out;
|
||||
if (!sw) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)
|
||||
{
|
||||
HWVoiceOut *hw = sw->hw;
|
||||
size_t live, dead, hw_free, sw_max, fe_max;
|
||||
size_t frames_in_max, frames_out_max, total_in, total_out;
|
||||
hwsamples = sw->hw->mix_buf->size;
|
||||
|
||||
live = sw->total_hw_samples_mixed;
|
||||
if (audio_bug(__func__, live > hw->mix_buf.size)) {
|
||||
dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
|
||||
if (audio_bug(__func__, live > hwsamples)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (live == hw->mix_buf.size) {
|
||||
if (live == hwsamples) {
|
||||
#ifdef DEBUG_OUT
|
||||
dolog ("%s is full %zu\n", sw->name, live);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
dead = hw->mix_buf.size - live;
|
||||
hw_free = audio_pcm_hw_get_free(hw);
|
||||
wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
|
||||
|
||||
dead = hwsamples - live;
|
||||
hw_free = audio_pcm_hw_get_free(sw->hw);
|
||||
hw_free = hw_free > live ? hw_free - live : 0;
|
||||
frames_out_max = MIN(dead, hw_free);
|
||||
sw_max = st_rate_frames_in(sw->rate, frames_out_max);
|
||||
fe_max = MIN(buf_len / sw->info.bytes_per_frame + sw->resample_buf.pos,
|
||||
sw->resample_buf.size);
|
||||
frames_in_max = MIN(sw_max, fe_max);
|
||||
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 (!frames_in_max) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (frames_in_max > sw->resample_buf.pos) {
|
||||
sw->conv(sw->resample_buf.buffer + sw->resample_buf.pos,
|
||||
buf, frames_in_max - sw->resample_buf.pos);
|
||||
if (!sw->hw->pcm_ops->volume_out) {
|
||||
mixeng_volume(sw->resample_buf.buffer + sw->resample_buf.pos,
|
||||
frames_in_max - sw->resample_buf.pos, &sw->vol);
|
||||
mixeng_volume(sw->buf, samples, &sw->vol);
|
||||
}
|
||||
}
|
||||
|
||||
audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max,
|
||||
&total_in, &total_out);
|
||||
|
||||
sw->total_hw_samples_mixed += total_out;
|
||||
sw->empty = sw->total_hw_samples_mixed == 0;
|
||||
|
||||
/*
|
||||
* Upsampling may leave one audio frame in the resample buffer. Decrement
|
||||
* total_in by one if there was a leftover frame from the previous resample
|
||||
* pass in the resample buffer. Increment total_in by one if the current
|
||||
* resample pass left one frame in the resample buffer.
|
||||
*/
|
||||
if (frames_in_max - total_in == 1) {
|
||||
/* copy one leftover audio frame to the beginning of the buffer */
|
||||
*sw->resample_buf.buffer = *(sw->resample_buf.buffer + total_in);
|
||||
total_in += 1 - sw->resample_buf.pos;
|
||||
sw->resample_buf.pos = 1;
|
||||
} else if (total_in >= sw->resample_buf.pos) {
|
||||
total_in -= sw->resample_buf.pos;
|
||||
sw->resample_buf.pos = 0;
|
||||
while (samples) {
|
||||
dead = hwsamples - live;
|
||||
left = hwsamples - wpos;
|
||||
blck = MIN (dead, left);
|
||||
if (!blck) {
|
||||
break;
|
||||
}
|
||||
isamp = samples;
|
||||
osamp = blck;
|
||||
st_rate_flow_mix (
|
||||
sw->rate,
|
||||
sw->buf + pos,
|
||||
sw->hw->mix_buf->samples + wpos,
|
||||
&isamp,
|
||||
&osamp
|
||||
);
|
||||
ret += isamp;
|
||||
samples -= isamp;
|
||||
pos += isamp;
|
||||
live += osamp;
|
||||
wpos = (wpos + osamp) % hwsamples;
|
||||
total += osamp;
|
||||
}
|
||||
|
||||
sw->total_hw_samples_mixed += total;
|
||||
sw->empty = sw->total_hw_samples_mixed == 0;
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
dolog (
|
||||
"%s: write size %zu written %zu total mixed %zu\n",
|
||||
SW_NAME(sw),
|
||||
buf_len / sw->info.bytes_per_frame,
|
||||
total_in,
|
||||
"%s: write size %zu ret %zu total sw %zu\n",
|
||||
SW_NAME (sw),
|
||||
size / sw->info.bytes_per_frame,
|
||||
ret,
|
||||
sw->total_hw_samples_mixed
|
||||
);
|
||||
#endif
|
||||
|
||||
return total_in * sw->info.bytes_per_frame;
|
||||
return ret * sw->info.bytes_per_frame;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
@@ -997,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;
|
||||
@@ -1006,21 +1011,33 @@ static size_t audio_get_avail (SWVoiceIn *sw)
|
||||
}
|
||||
|
||||
live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
if (audio_bug(__func__, live > sw->hw->conv_buf.size)) {
|
||||
dolog("live=%zu sw->hw->conv_buf.size=%zu\n", live,
|
||||
sw->hw->conv_buf.size);
|
||||
if (audio_bug(__func__, live > sw->hw->conv_buf->size)) {
|
||||
dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live,
|
||||
sw->hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ldebug (
|
||||
"%s: get_avail live %zu frontend frames %u\n",
|
||||
"%s: get_avail live %zu frontend frames %zu\n",
|
||||
SW_NAME (sw),
|
||||
live, st_rate_frames_out(sw->rate, live)
|
||||
live, audio_frontend_frames_in(sw, live)
|
||||
);
|
||||
|
||||
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)
|
||||
{
|
||||
size_t live, dead;
|
||||
@@ -1031,17 +1048,17 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
||||
|
||||
live = sw->total_hw_samples_mixed;
|
||||
|
||||
if (audio_bug(__func__, live > sw->hw->mix_buf.size)) {
|
||||
dolog("live=%zu sw->hw->mix_buf.size=%zu\n", live,
|
||||
sw->hw->mix_buf.size);
|
||||
if (audio_bug(__func__, live > sw->hw->mix_buf->size)) {
|
||||
dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live,
|
||||
sw->hw->mix_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dead = sw->hw->mix_buf.size - live;
|
||||
dead = sw->hw->mix_buf->size - live;
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
dolog("%s: get_free live %zu dead %zu frontend frames %u\n",
|
||||
SW_NAME(sw), live, dead, st_rate_frames_in(sw->rate, dead));
|
||||
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 dead;
|
||||
@@ -1057,40 +1074,32 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
|
||||
|
||||
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
|
||||
SWVoiceOut *sw = &sc->sw;
|
||||
size_t rpos2 = rpos;
|
||||
int rpos2 = rpos;
|
||||
|
||||
n = samples;
|
||||
while (n) {
|
||||
size_t till_end_of_hw = hw->mix_buf.size - rpos2;
|
||||
size_t to_read = MIN(till_end_of_hw, n);
|
||||
size_t live, frames_in, frames_out;
|
||||
size_t till_end_of_hw = hw->mix_buf->size - rpos2;
|
||||
size_t to_write = MIN(till_end_of_hw, n);
|
||||
size_t bytes = to_write * hw->info.bytes_per_frame;
|
||||
size_t written;
|
||||
|
||||
sw->resample_buf.buffer = hw->mix_buf.buffer + rpos2;
|
||||
sw->resample_buf.size = to_read;
|
||||
live = sw->total_hw_samples_mixed;
|
||||
|
||||
audio_pcm_sw_resample_out(sw,
|
||||
to_read, sw->hw->mix_buf.size - live,
|
||||
&frames_in, &frames_out);
|
||||
|
||||
sw->total_hw_samples_mixed += frames_out;
|
||||
sw->empty = sw->total_hw_samples_mixed == 0;
|
||||
|
||||
if (to_read - frames_in) {
|
||||
dolog("Could not mix %zu frames into a capture "
|
||||
sw->buf = hw->mix_buf->samples + rpos2;
|
||||
written = audio_pcm_sw_write (sw, NULL, bytes);
|
||||
if (written - bytes) {
|
||||
dolog("Could not mix %zu bytes into a capture "
|
||||
"buffer, mixed %zu\n",
|
||||
to_read, frames_in);
|
||||
bytes, written);
|
||||
break;
|
||||
}
|
||||
n -= to_read;
|
||||
rpos2 = (rpos2 + to_read) % hw->mix_buf.size;
|
||||
n -= to_write;
|
||||
rpos2 = (rpos2 + to_write) % hw->mix_buf->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n = MIN(samples, hw->mix_buf.size - rpos);
|
||||
mixeng_clear(hw->mix_buf.buffer + rpos, n);
|
||||
mixeng_clear(hw->mix_buf.buffer, samples - n);
|
||||
n = MIN(samples, hw->mix_buf->size - rpos);
|
||||
mixeng_clear(hw->mix_buf->samples + rpos, n);
|
||||
mixeng_clear(hw->mix_buf->samples, samples - n);
|
||||
}
|
||||
|
||||
static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
||||
@@ -1116,7 +1125,7 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
||||
|
||||
live -= proc;
|
||||
clipped += proc;
|
||||
hw->mix_buf.pos = (hw->mix_buf.pos + proc) % hw->mix_buf.size;
|
||||
hw->mix_buf->pos = (hw->mix_buf->pos + proc) % hw->mix_buf->size;
|
||||
|
||||
if (proc == 0 || proc < decr) {
|
||||
break;
|
||||
@@ -1170,14 +1179,12 @@ static void audio_run_out (AudioState *s)
|
||||
size_t free;
|
||||
|
||||
if (hw_free > sw->total_hw_samples_mixed) {
|
||||
free = st_rate_frames_in(sw->rate,
|
||||
free = audio_frontend_frames_out(sw,
|
||||
MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
|
||||
} else {
|
||||
free = 0;
|
||||
}
|
||||
if (free > sw->resample_buf.pos) {
|
||||
free = MIN(free, sw->resample_buf.size)
|
||||
- sw->resample_buf.pos;
|
||||
if (free > 0) {
|
||||
sw->callback.fn(sw->callback.opaque,
|
||||
free * sw->info.bytes_per_frame);
|
||||
}
|
||||
@@ -1189,8 +1196,8 @@ static void audio_run_out (AudioState *s)
|
||||
live = 0;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, live > hw->mix_buf.size)) {
|
||||
dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
|
||||
if (audio_bug(__func__, live > hw->mix_buf->size)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1218,13 +1225,13 @@ static void audio_run_out (AudioState *s)
|
||||
continue;
|
||||
}
|
||||
|
||||
prev_rpos = hw->mix_buf.pos;
|
||||
prev_rpos = hw->mix_buf->pos;
|
||||
played = audio_pcm_hw_run_out(hw, live);
|
||||
replay_audio_out(&played);
|
||||
if (audio_bug(__func__, hw->mix_buf.pos >= hw->mix_buf.size)) {
|
||||
dolog("hw->mix_buf.pos=%zu hw->mix_buf.size=%zu played=%zu\n",
|
||||
hw->mix_buf.pos, hw->mix_buf.size, played);
|
||||
hw->mix_buf.pos = 0;
|
||||
if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) {
|
||||
dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n",
|
||||
hw->mix_buf->pos, hw->mix_buf->size, played);
|
||||
hw->mix_buf->pos = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
@@ -1305,10 +1312,10 @@ static void audio_run_in (AudioState *s)
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
captured = audio_pcm_hw_run_in(
|
||||
hw, hw->conv_buf.size - audio_pcm_hw_get_live_in(hw));
|
||||
hw, hw->conv_buf->size - audio_pcm_hw_get_live_in(hw));
|
||||
}
|
||||
replay_audio_in(&captured, hw->conv_buf.buffer, &hw->conv_buf.pos,
|
||||
hw->conv_buf.size);
|
||||
replay_audio_in(&captured, hw->conv_buf->samples, &hw->conv_buf->pos,
|
||||
hw->conv_buf->size);
|
||||
|
||||
min = audio_pcm_hw_find_min_in (hw);
|
||||
hw->total_samples_captured += captured - min;
|
||||
@@ -1321,9 +1328,8 @@ static void audio_run_in (AudioState *s)
|
||||
size_t sw_avail = audio_get_avail(sw);
|
||||
size_t avail;
|
||||
|
||||
avail = st_rate_frames_out(sw->rate, sw_avail);
|
||||
avail = audio_frontend_frames_in(sw, sw_avail);
|
||||
if (avail > 0) {
|
||||
avail = MIN(avail, sw->resample_buf.size);
|
||||
sw->callback.fn(sw->callback.opaque,
|
||||
avail * sw->info.bytes_per_frame);
|
||||
}
|
||||
@@ -1342,14 +1348,14 @@ static void audio_run_capture (AudioState *s)
|
||||
SWVoiceOut *sw;
|
||||
|
||||
captured = live = audio_pcm_hw_get_live_out (hw, NULL);
|
||||
rpos = hw->mix_buf.pos;
|
||||
rpos = hw->mix_buf->pos;
|
||||
while (live) {
|
||||
size_t left = hw->mix_buf.size - rpos;
|
||||
size_t left = hw->mix_buf->size - rpos;
|
||||
size_t to_capture = MIN(live, left);
|
||||
struct st_sample *src;
|
||||
struct capture_callback *cb;
|
||||
|
||||
src = hw->mix_buf.buffer + rpos;
|
||||
src = hw->mix_buf->samples + rpos;
|
||||
hw->clip (cap->buf, src, to_capture);
|
||||
mixeng_clear (src, to_capture);
|
||||
|
||||
@@ -1357,10 +1363,10 @@ static void audio_run_capture (AudioState *s)
|
||||
cb->ops.capture (cb->opaque, cap->buf,
|
||||
to_capture * hw->info.bytes_per_frame);
|
||||
}
|
||||
rpos = (rpos + to_capture) % hw->mix_buf.size;
|
||||
rpos = (rpos + to_capture) % hw->mix_buf->size;
|
||||
live -= to_capture;
|
||||
}
|
||||
hw->mix_buf.pos = rpos;
|
||||
hw->mix_buf->pos = rpos;
|
||||
|
||||
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
|
||||
if (!sw->active && sw->empty) {
|
||||
@@ -1919,7 +1925,7 @@ CaptureVoiceOut *AUD_add_capture(
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame);
|
||||
cap->buf = g_malloc0_n(hw->mix_buf->size, hw->info.bytes_per_frame);
|
||||
|
||||
if (hw->info.is_float) {
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
|
||||
@@ -1971,7 +1977,7 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
|
||||
sw = sw1;
|
||||
}
|
||||
QLIST_REMOVE (cap, entries);
|
||||
g_free(cap->hw.mix_buf.buffer);
|
||||
g_free (cap->hw.mix_buf);
|
||||
g_free (cap->buf);
|
||||
g_free (cap);
|
||||
}
|
||||
@@ -2029,50 +2035,29 @@ void audio_create_pdos(Audiodev *dev)
|
||||
switch (dev->driver) {
|
||||
#define CASE(DRIVER, driver, pdo_name) \
|
||||
case AUDIODEV_DRIVER_##DRIVER: \
|
||||
if (!dev->u.driver.in) { \
|
||||
if (!dev->u.driver.has_in) { \
|
||||
dev->u.driver.in = g_malloc0( \
|
||||
sizeof(Audiodev##pdo_name##PerDirectionOptions)); \
|
||||
dev->u.driver.has_in = true; \
|
||||
} \
|
||||
if (!dev->u.driver.out) { \
|
||||
if (!dev->u.driver.has_out) { \
|
||||
dev->u.driver.out = g_malloc0( \
|
||||
sizeof(Audiodev##pdo_name##PerDirectionOptions)); \
|
||||
dev->u.driver.has_out = true; \
|
||||
} \
|
||||
break
|
||||
|
||||
CASE(NONE, none, );
|
||||
#ifdef CONFIG_AUDIO_ALSA
|
||||
CASE(ALSA, alsa, Alsa);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_COREAUDIO
|
||||
CASE(COREAUDIO, coreaudio, Coreaudio);
|
||||
#endif
|
||||
#ifdef CONFIG_DBUS_DISPLAY
|
||||
CASE(DBUS, dbus, );
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_DSOUND
|
||||
CASE(DSOUND, dsound, );
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_JACK
|
||||
CASE(JACK, jack, Jack);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_OSS
|
||||
CASE(OSS, oss, Oss);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_PA
|
||||
CASE(PA, pa, Pa);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_PIPEWIRE
|
||||
CASE(PIPEWIRE, pipewire, Pipewire);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_SDL
|
||||
CASE(SDL, sdl, Sdl);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_SNDIO
|
||||
CASE(SNDIO, sndio, );
|
||||
#endif
|
||||
#ifdef CONFIG_SPICE
|
||||
CASE(SPICE, spice, );
|
||||
#endif
|
||||
CASE(WAV, wav, );
|
||||
|
||||
case AUDIODEV_DRIVER__MAX:
|
||||
@@ -2328,13 +2313,3 @@ size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
AudiodevList *qmp_query_audiodevs(Error **errp)
|
||||
{
|
||||
AudiodevList *ret = NULL;
|
||||
AudiodevListEntry *e;
|
||||
QSIMPLEQ_FOREACH(e, &audiodevs, next) {
|
||||
QAPI_LIST_PREPEND(ret, QAPI_CLONE(Audiodev, e->dev));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ typedef struct SWVoiceCap SWVoiceCap;
|
||||
|
||||
typedef struct STSampleBuffer {
|
||||
size_t pos, size;
|
||||
st_sample *buffer;
|
||||
st_sample samples[];
|
||||
} STSampleBuffer;
|
||||
|
||||
typedef struct HWVoiceOut {
|
||||
@@ -71,7 +71,7 @@ typedef struct HWVoiceOut {
|
||||
f_sample *clip;
|
||||
uint64_t ts_helper;
|
||||
|
||||
STSampleBuffer mix_buf;
|
||||
STSampleBuffer *mix_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
|
||||
@@ -93,7 +93,7 @@ typedef struct HWVoiceIn {
|
||||
size_t total_samples_captured;
|
||||
uint64_t ts_helper;
|
||||
|
||||
STSampleBuffer conv_buf;
|
||||
STSampleBuffer *conv_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
|
||||
@@ -108,7 +108,8 @@ struct SWVoiceOut {
|
||||
AudioState *s;
|
||||
struct audio_pcm_info info;
|
||||
t_sample *conv;
|
||||
STSampleBuffer resample_buf;
|
||||
int64_t ratio;
|
||||
struct st_sample *buf;
|
||||
void *rate;
|
||||
size_t total_hw_samples_mixed;
|
||||
int active;
|
||||
@@ -125,9 +126,10 @@ struct SWVoiceIn {
|
||||
AudioState *s;
|
||||
int active;
|
||||
struct audio_pcm_info info;
|
||||
int64_t ratio;
|
||||
void *rate;
|
||||
size_t total_hw_samples_acquired;
|
||||
STSampleBuffer resample_buf;
|
||||
struct st_sample *buf;
|
||||
f_sample *clip;
|
||||
HWVoiceIn *hw;
|
||||
char *name;
|
||||
@@ -143,14 +145,14 @@ struct audio_driver {
|
||||
void *(*init) (Audiodev *);
|
||||
void (*fini) (void *);
|
||||
#ifdef CONFIG_GIO
|
||||
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager, bool p2p);
|
||||
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager);
|
||||
#endif
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices_out;
|
||||
int max_voices_in;
|
||||
size_t voice_size_out;
|
||||
size_t voice_size_in;
|
||||
int voice_size_out;
|
||||
int voice_size_in;
|
||||
QLIST_ENTRY(audio_driver) next;
|
||||
};
|
||||
|
||||
@@ -249,6 +251,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
|
||||
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
|
||||
|
||||
int audio_bug (const char *funcname, int cond);
|
||||
void *audio_calloc (const char *funcname, int nmemb, size_t size);
|
||||
|
||||
void audio_run(AudioState *s, const char *msg);
|
||||
|
||||
@@ -291,6 +294,9 @@ static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
|
||||
#define ldebug(fmt, ...) (void)0
|
||||
#endif
|
||||
|
||||
#define AUDIO_STRINGIFY_(n) #n
|
||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
||||
|
||||
typedef struct AudiodevListEntry {
|
||||
Audiodev *dev;
|
||||
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
|
||||
|
@@ -35,8 +35,8 @@
|
||||
|
||||
static uint32_t toui32(const char *str)
|
||||
{
|
||||
uint64_t ret;
|
||||
if (parse_uint_full(str, 10, &ret) || ret > UINT32_MAX) {
|
||||
unsigned long long ret;
|
||||
if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
|
||||
dolog("Invalid integer value `%s'\n", str);
|
||||
exit(1);
|
||||
}
|
||||
@@ -62,12 +62,15 @@ static void get_int(const char *env, uint32_t *dst, bool *has_dst)
|
||||
}
|
||||
}
|
||||
|
||||
static void get_str(const char *env, char **dst)
|
||||
static void get_str(const char *env, char **dst, bool *has_dst)
|
||||
{
|
||||
const char *val = getenv(env);
|
||||
if (val) {
|
||||
g_free(*dst);
|
||||
if (*has_dst) {
|
||||
g_free(*dst);
|
||||
}
|
||||
*dst = g_strdup(val);
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +93,6 @@ static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_DSOUND)
|
||||
static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
|
||||
{
|
||||
const char *val = getenv(env);
|
||||
@@ -99,20 +101,15 @@ static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_COREAUDIO) || \
|
||||
defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \
|
||||
defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS)
|
||||
static uint32_t frames_to_usecs(uint32_t frames,
|
||||
AudiodevPerDirectionOptions *pdo)
|
||||
{
|
||||
uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
|
||||
return (frames * 1000000 + freq / 2) / freq;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_COREAUDIO
|
||||
|
||||
static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||
AudiodevPerDirectionOptions *pdo)
|
||||
{
|
||||
@@ -122,19 +119,14 @@ static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \
|
||||
defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS)
|
||||
static uint32_t samples_to_usecs(uint32_t samples,
|
||||
AudiodevPerDirectionOptions *pdo)
|
||||
{
|
||||
uint32_t channels = pdo->has_channels ? pdo->channels : 2;
|
||||
return frames_to_usecs(samples / channels, pdo);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL)
|
||||
static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||
AudiodevPerDirectionOptions *pdo)
|
||||
{
|
||||
@@ -144,9 +136,7 @@ static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS)
|
||||
static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
|
||||
{
|
||||
AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
|
||||
@@ -163,11 +153,8 @@ static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* backend specific functions */
|
||||
|
||||
#ifdef CONFIG_AUDIO_ALSA
|
||||
/* ALSA */
|
||||
static void handle_alsa_per_direction(
|
||||
AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
|
||||
@@ -182,7 +169,7 @@ static void handle_alsa_per_direction(
|
||||
get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
|
||||
|
||||
strcpy(buf + len, "DEV");
|
||||
get_str(buf, &apdo->dev);
|
||||
get_str(buf, &apdo->dev, &apdo->has_dev);
|
||||
|
||||
strcpy(buf + len, "SIZE_IN_USEC");
|
||||
get_bool(buf, &size_in_usecs, &dummy);
|
||||
@@ -213,9 +200,7 @@ static void handle_alsa(Audiodev *dev)
|
||||
get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
|
||||
&aopt->threshold, &aopt->has_threshold);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_COREAUDIO
|
||||
/* coreaudio */
|
||||
static void handle_coreaudio(Audiodev *dev)
|
||||
{
|
||||
@@ -228,9 +213,7 @@ static void handle_coreaudio(Audiodev *dev)
|
||||
&dev->u.coreaudio.out->buffer_count,
|
||||
&dev->u.coreaudio.out->has_buffer_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_DSOUND
|
||||
/* dsound */
|
||||
static void handle_dsound(Audiodev *dev)
|
||||
{
|
||||
@@ -245,16 +228,14 @@ static void handle_dsound(Audiodev *dev)
|
||||
&dev->u.dsound.in->has_buffer_length,
|
||||
dev->u.dsound.in);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_OSS
|
||||
/* OSS */
|
||||
static void handle_oss_per_direction(
|
||||
AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
|
||||
const char *dev_env)
|
||||
{
|
||||
get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
|
||||
get_str(dev_env, &opdo->dev);
|
||||
get_str(dev_env, &opdo->dev, &opdo->has_dev);
|
||||
|
||||
get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
|
||||
&opdo->buffer_length, &opdo->has_buffer_length,
|
||||
@@ -275,14 +256,12 @@ static void handle_oss(Audiodev *dev)
|
||||
get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
|
||||
get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_PA
|
||||
/* pulseaudio */
|
||||
static void handle_pa_per_direction(
|
||||
AudiodevPaPerDirectionOptions *ppdo, const char *env)
|
||||
{
|
||||
get_str(env, &ppdo->name);
|
||||
get_str(env, &ppdo->name, &ppdo->has_name);
|
||||
}
|
||||
|
||||
static void handle_pa(Audiodev *dev)
|
||||
@@ -299,11 +278,9 @@ static void handle_pa(Audiodev *dev)
|
||||
&dev->u.pa.out->has_buffer_length,
|
||||
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
|
||||
|
||||
get_str("QEMU_PA_SERVER", &dev->u.pa.server);
|
||||
get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_SDL
|
||||
/* SDL */
|
||||
static void handle_sdl(Audiodev *dev)
|
||||
{
|
||||
@@ -312,7 +289,6 @@ static void handle_sdl(Audiodev *dev)
|
||||
&dev->u.sdl.out->has_buffer_length,
|
||||
qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* wav */
|
||||
static void handle_wav(Audiodev *dev)
|
||||
@@ -323,7 +299,7 @@ static void handle_wav(Audiodev *dev)
|
||||
&dev->u.wav.out->has_format);
|
||||
get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
|
||||
&dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
|
||||
get_str("QEMU_WAV_PATH", &dev->u.wav.path);
|
||||
get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path);
|
||||
}
|
||||
|
||||
/* general */
|
||||
@@ -372,41 +348,29 @@ static AudiodevListEntry *legacy_opt(const char *drvname)
|
||||
}
|
||||
|
||||
switch (e->dev->driver) {
|
||||
#ifdef CONFIG_AUDIO_ALSA
|
||||
case AUDIODEV_DRIVER_ALSA:
|
||||
handle_alsa(e->dev);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_COREAUDIO
|
||||
case AUDIODEV_DRIVER_COREAUDIO:
|
||||
handle_coreaudio(e->dev);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_DSOUND
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
handle_dsound(e->dev);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_OSS
|
||||
case AUDIODEV_DRIVER_OSS:
|
||||
handle_oss(e->dev);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_PA
|
||||
case AUDIODEV_DRIVER_PA:
|
||||
handle_pa(e->dev);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIO_SDL
|
||||
case AUDIODEV_DRIVER_SDL:
|
||||
handle_sdl(e->dev);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case AUDIODEV_DRIVER_WAV:
|
||||
handle_wav(e->dev);
|
||||
|
@@ -40,7 +40,7 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
||||
struct audio_driver *drv)
|
||||
{
|
||||
int max_voices = glue (drv->max_voices_, TYPE);
|
||||
size_t voice_size = glue(drv->voice_size_, TYPE);
|
||||
int voice_size = glue (drv->voice_size_, TYPE);
|
||||
|
||||
if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
|
||||
if (!max_voices) {
|
||||
@@ -63,17 +63,16 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, voice_size && !max_voices)) {
|
||||
dolog("drv=`%s' voice_size=%zu max_voices=0\n",
|
||||
drv->name, voice_size);
|
||||
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
|
||||
drv->name, voice_size);
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
g_free(hw->buf_emul);
|
||||
g_free(HWBUF.buffer);
|
||||
HWBUF.buffer = NULL;
|
||||
HWBUF.size = 0;
|
||||
g_free (HWBUF);
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
||||
static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
|
||||
@@ -84,67 +83,56 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
|
||||
dolog("Attempted to allocate empty buffer\n");
|
||||
}
|
||||
|
||||
HWBUF.buffer = g_new0(st_sample, samples);
|
||||
HWBUF.size = samples;
|
||||
HWBUF.pos = 0;
|
||||
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
|
||||
HWBUF->size = samples;
|
||||
} else {
|
||||
HWBUF.buffer = NULL;
|
||||
HWBUF.size = 0;
|
||||
HWBUF = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
g_free(sw->resample_buf.buffer);
|
||||
sw->resample_buf.buffer = NULL;
|
||||
sw->resample_buf.size = 0;
|
||||
g_free (sw->buf);
|
||||
|
||||
if (sw->rate) {
|
||||
st_rate_stop (sw->rate);
|
||||
}
|
||||
|
||||
sw->buf = NULL;
|
||||
sw->rate = NULL;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
HW *hw = sw->hw;
|
||||
uint64_t samples;
|
||||
int samples;
|
||||
|
||||
if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
samples = muldiv64(HWBUF.size, sw->info.freq, hw->info.freq);
|
||||
if (samples == 0) {
|
||||
uint64_t f_fe_min;
|
||||
uint64_t f_be = (uint32_t)hw->info.freq;
|
||||
#ifdef DAC
|
||||
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
|
||||
#else
|
||||
samples = (int64_t)sw->HWBUF->size * sw->ratio >> 32;
|
||||
#endif
|
||||
|
||||
/* f_fe_min = ceil(1 [frames] * f_be [Hz] / size_be [frames]) */
|
||||
f_fe_min = (f_be + HWBUF.size - 1) / HWBUF.size;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
AUDIO_CAP ": The guest selected a " NAME " sample rate"
|
||||
" of %d Hz for %s. Only sample rates >= %" PRIu64 " Hz"
|
||||
" are supported.\n",
|
||||
sw->info.freq, sw->name, f_fe_min);
|
||||
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
|
||||
if (!sw->buf) {
|
||||
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
|
||||
SW_NAME (sw), samples);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate one additional audio frame that is needed for upsampling
|
||||
* if the resample buffer size is small. For large buffer sizes take
|
||||
* care of overflows and truncation.
|
||||
*/
|
||||
samples = samples < SIZE_MAX ? samples + 1 : SIZE_MAX;
|
||||
sw->resample_buf.buffer = g_new0(st_sample, samples);
|
||||
sw->resample_buf.size = samples;
|
||||
sw->resample_buf.pos = 0;
|
||||
|
||||
#ifdef DAC
|
||||
sw->rate = st_rate_start(sw->info.freq, hw->info.freq);
|
||||
sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
|
||||
#else
|
||||
sw->rate = st_rate_start(hw->info.freq, sw->info.freq);
|
||||
sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
|
||||
#endif
|
||||
|
||||
if (!sw->rate) {
|
||||
g_free (sw->buf);
|
||||
sw->buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -161,8 +149,11 @@ static int glue (audio_pcm_sw_init_, TYPE) (
|
||||
sw->hw = hw;
|
||||
sw->active = 0;
|
||||
#ifdef DAC
|
||||
sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
|
||||
sw->total_hw_samples_mixed = 0;
|
||||
sw->empty = 1;
|
||||
#else
|
||||
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
|
||||
#endif
|
||||
|
||||
if (sw->info.is_float) {
|
||||
@@ -273,11 +264,13 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since glue(s->nb_hw_voices_, TYPE) is != 0, glue(drv->voice_size_, TYPE)
|
||||
* is guaranteed to be != 0. See the audio_init_nb_voices_* functions.
|
||||
*/
|
||||
hw = g_malloc0(glue(drv->voice_size_, TYPE));
|
||||
hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE));
|
||||
if (!hw) {
|
||||
dolog ("Can not allocate voice `%s' size %d\n",
|
||||
drv->name, glue (drv->voice_size_, TYPE));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw->s = s;
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
|
||||
@@ -333,51 +326,27 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
switch (dev->driver) {
|
||||
case AUDIODEV_DRIVER_NONE:
|
||||
return dev->u.none.TYPE;
|
||||
#ifdef CONFIG_AUDIO_ALSA
|
||||
case AUDIODEV_DRIVER_ALSA:
|
||||
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_COREAUDIO
|
||||
case AUDIODEV_DRIVER_COREAUDIO:
|
||||
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
|
||||
dev->u.coreaudio.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_DBUS_DISPLAY
|
||||
case AUDIODEV_DRIVER_DBUS:
|
||||
return dev->u.dbus.TYPE;
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_DSOUND
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
return dev->u.dsound.TYPE;
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_JACK
|
||||
case AUDIODEV_DRIVER_JACK:
|
||||
return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_OSS
|
||||
case AUDIODEV_DRIVER_OSS:
|
||||
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_PA
|
||||
case AUDIODEV_DRIVER_PA:
|
||||
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_PIPEWIRE
|
||||
case AUDIODEV_DRIVER_PIPEWIRE:
|
||||
return qapi_AudiodevPipewirePerDirectionOptions_base(dev->u.pipewire.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_SDL
|
||||
case AUDIODEV_DRIVER_SDL:
|
||||
return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE);
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_SNDIO
|
||||
case AUDIODEV_DRIVER_SNDIO:
|
||||
return dev->u.sndio.TYPE;
|
||||
#endif
|
||||
#ifdef CONFIG_SPICE
|
||||
case AUDIODEV_DRIVER_SPICE:
|
||||
return dev->u.spice.TYPE;
|
||||
#endif
|
||||
case AUDIODEV_DRIVER_WAV:
|
||||
return dev->u.wav.TYPE;
|
||||
|
||||
@@ -429,28 +398,33 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
|
||||
hw_as = *as;
|
||||
}
|
||||
|
||||
sw = g_new0(SW, 1);
|
||||
sw = audio_calloc(__func__, 1, sizeof(*sw));
|
||||
if (!sw) {
|
||||
dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
|
||||
sw_name ? sw_name : "unknown", sizeof (*sw));
|
||||
goto err1;
|
||||
}
|
||||
sw->s = s;
|
||||
|
||||
hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
|
||||
if (!hw) {
|
||||
dolog("Could not create a backend for voice `%s'\n", sw_name);
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
|
||||
|
||||
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
|
||||
goto err2;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return sw;
|
||||
|
||||
err2:
|
||||
err3:
|
||||
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_gc_, TYPE) (&hw);
|
||||
err2:
|
||||
g_free (sw);
|
||||
err1:
|
||||
g_free(sw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -521,8 +495,8 @@ SW *glue (AUD_open_, TYPE) (
|
||||
HW *hw = sw->hw;
|
||||
|
||||
if (!hw) {
|
||||
dolog("Internal logic error: voice `%s' has no backend\n",
|
||||
SW_NAME(sw));
|
||||
dolog ("Internal logic error voice `%s' has no hardware store\n",
|
||||
SW_NAME (sw));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -533,6 +507,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||
} else {
|
||||
sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
|
||||
if (!sw) {
|
||||
dolog ("Failed to create voice `%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,6 @@
|
||||
|
||||
typedef struct DBusAudio {
|
||||
GDBusObjectManagerServer *server;
|
||||
bool p2p;
|
||||
GDBusObjectSkeleton *audio;
|
||||
QemuDBusDisplay1Audio *iface;
|
||||
GHashTable *out_listeners;
|
||||
@@ -449,8 +448,7 @@ dbus_audio_register_listener(AudioState *s,
|
||||
bool out)
|
||||
{
|
||||
DBusAudio *da = s->drv_opaque;
|
||||
const char *sender =
|
||||
da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation);
|
||||
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;
|
||||
@@ -593,7 +591,7 @@ dbus_audio_register_in_listener(AudioState *s,
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
|
||||
dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server)
|
||||
{
|
||||
DBusAudio *da = s->drv_opaque;
|
||||
|
||||
@@ -601,7 +599,6 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
|
||||
g_assert(!da->server);
|
||||
|
||||
da->server = g_object_ref(server);
|
||||
da->p2p = p2p;
|
||||
|
||||
da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
|
||||
da->iface = qemu_dbus_display1_audio_skeleton_new();
|
||||
|
@@ -1,6 +1,5 @@
|
||||
system_ss.add([spice_headers, files('audio.c')])
|
||||
system_ss.add(files(
|
||||
'audio-hmp-cmds.c',
|
||||
softmmu_ss.add([spice_headers, files('audio.c')])
|
||||
softmmu_ss.add(files(
|
||||
'audio_legacy.c',
|
||||
'mixeng.c',
|
||||
'noaudio.c',
|
||||
@@ -8,8 +7,8 @@ system_ss.add(files(
|
||||
'wavcapture.c',
|
||||
))
|
||||
|
||||
system_ss.add(when: coreaudio, if_true: files('coreaudio.m'))
|
||||
system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))
|
||||
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 : [
|
||||
@@ -19,7 +18,6 @@ foreach m : [
|
||||
['sdl', sdl, files('sdlaudio.c')],
|
||||
['jack', jack, files('jackaudio.c')],
|
||||
['sndio', sndio, files('sndioaudio.c')],
|
||||
['pipewire', pipewire, files('pwaudio.c')],
|
||||
['spice', spice, files('spiceaudio.c')]
|
||||
]
|
||||
if m[1].found()
|
||||
|
@@ -414,7 +414,12 @@ struct rate {
|
||||
*/
|
||||
void *st_rate_start (int inrate, int outrate)
|
||||
{
|
||||
struct rate *rate = g_new0(struct rate, 1);
|
||||
struct rate *rate = audio_calloc(__func__, 1, sizeof(*rate));
|
||||
|
||||
if (!rate) {
|
||||
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate->opos = 0;
|
||||
|
||||
@@ -440,86 +445,6 @@ void st_rate_stop (void *opaque)
|
||||
g_free (opaque);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_rate_frames_out() - returns the number of frames the resampling code
|
||||
* generates from frames_in frames
|
||||
*
|
||||
* @opaque: pointer to struct rate
|
||||
* @frames_in: number of frames
|
||||
*
|
||||
* When upsampling, there may be more than one correct result. In this case,
|
||||
* the function returns the maximum number of output frames the resampling
|
||||
* code can generate.
|
||||
*/
|
||||
uint32_t st_rate_frames_out(void *opaque, uint32_t frames_in)
|
||||
{
|
||||
struct rate *rate = opaque;
|
||||
uint64_t opos_end, opos_delta;
|
||||
uint32_t ipos_end;
|
||||
uint32_t frames_out;
|
||||
|
||||
if (rate->opos_inc == 1ULL << 32) {
|
||||
return frames_in;
|
||||
}
|
||||
|
||||
/* no output frame without at least one input frame */
|
||||
if (!frames_in) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* last frame read was at rate->ipos - 1 */
|
||||
ipos_end = rate->ipos - 1 + frames_in;
|
||||
opos_end = (uint64_t)ipos_end << 32;
|
||||
|
||||
/* last frame written was at rate->opos - rate->opos_inc */
|
||||
if (opos_end + rate->opos_inc <= rate->opos) {
|
||||
return 0;
|
||||
}
|
||||
opos_delta = opos_end - rate->opos + rate->opos_inc;
|
||||
frames_out = opos_delta / rate->opos_inc;
|
||||
|
||||
return opos_delta % rate->opos_inc ? frames_out : frames_out - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_rate_frames_in() - returns the number of frames needed to
|
||||
* get frames_out frames after resampling
|
||||
*
|
||||
* @opaque: pointer to struct rate
|
||||
* @frames_out: number of frames
|
||||
*
|
||||
* When downsampling, there may be more than one correct result. In this
|
||||
* case, the function returns the maximum number of input frames needed.
|
||||
*/
|
||||
uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out)
|
||||
{
|
||||
struct rate *rate = opaque;
|
||||
uint64_t opos_start, opos_end;
|
||||
uint32_t ipos_start, ipos_end;
|
||||
|
||||
if (rate->opos_inc == 1ULL << 32) {
|
||||
return frames_out;
|
||||
}
|
||||
|
||||
if (frames_out) {
|
||||
opos_start = rate->opos;
|
||||
ipos_start = rate->ipos;
|
||||
} else {
|
||||
uint64_t offset;
|
||||
|
||||
/* add offset = ceil(opos_inc) to opos and ipos to avoid an underflow */
|
||||
offset = (rate->opos_inc + (1ULL << 32) - 1) & ~((1ULL << 32) - 1);
|
||||
opos_start = rate->opos + offset;
|
||||
ipos_start = rate->ipos + (offset >> 32);
|
||||
}
|
||||
/* last frame written was at opos_start - rate->opos_inc */
|
||||
opos_end = opos_start - rate->opos_inc + rate->opos_inc * frames_out;
|
||||
ipos_end = (opos_end >> 32) + 1;
|
||||
|
||||
/* last frame read was at ipos_start - 1 */
|
||||
return ipos_end + 1 > ipos_start ? ipos_end + 1 - ipos_start : 0;
|
||||
}
|
||||
|
||||
void mixeng_clear (struct st_sample *buf, int len)
|
||||
{
|
||||
memset (buf, 0, len * sizeof (struct st_sample));
|
||||
|
@@ -52,8 +52,6 @@ void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
size_t *isamp, size_t *osamp);
|
||||
void st_rate_stop (void *opaque);
|
||||
uint32_t st_rate_frames_out(void *opaque, uint32_t frames_in);
|
||||
uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out);
|
||||
void mixeng_clear (struct st_sample *buf, int len);
|
||||
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);
|
||||
|
||||
|
@@ -252,7 +252,7 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||
audio_buf_info abinfo;
|
||||
int fmt, freq, nchannels;
|
||||
int setfragment = 1;
|
||||
const char *dspname = opdo->dev ?: "/dev/dsp";
|
||||
const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
#ifdef USE_DSP_POLICY
|
||||
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
|
||||
@@ -745,8 +745,10 @@ static void *oss_audio_init(Audiodev *dev)
|
||||
oss_init_per_direction(oopts->in);
|
||||
oss_init_per_direction(oopts->out);
|
||||
|
||||
if (access(oopts->in->dev ?: "/dev/dsp", R_OK | W_OK) < 0 ||
|
||||
access(oopts->out->dev ?: "/dev/dsp", R_OK | W_OK) < 0) {
|
||||
if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
|
||||
R_OK | W_OK) < 0 ||
|
||||
access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
|
||||
R_OK | W_OK) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return dev;
|
||||
|
@@ -536,9 +536,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
c,
|
||||
ppdo->stream_name ?: g->dev->id,
|
||||
ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
|
||||
PA_STREAM_PLAYBACK,
|
||||
ppdo->name,
|
||||
ppdo->has_name ? ppdo->name : NULL,
|
||||
&ss,
|
||||
&ba, /* buffering attributes */
|
||||
&error
|
||||
@@ -585,9 +585,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
c,
|
||||
ppdo->stream_name ?: g->dev->id,
|
||||
ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
|
||||
PA_STREAM_RECORD,
|
||||
ppdo->name,
|
||||
ppdo->has_name ? ppdo->name : NULL,
|
||||
&ss,
|
||||
&ba, /* buffering attributes */
|
||||
&error
|
||||
@@ -827,7 +827,7 @@ static void *qpa_audio_init(Audiodev *dev)
|
||||
|
||||
assert(dev->driver == AUDIODEV_DRIVER_PA);
|
||||
|
||||
if (!popts->server) {
|
||||
if (!popts->has_server) {
|
||||
char pidfile[64];
|
||||
char *runtime;
|
||||
struct stat st;
|
||||
@@ -850,7 +850,7 @@ static void *qpa_audio_init(Audiodev *dev)
|
||||
}
|
||||
|
||||
g = g_new0(paaudio, 1);
|
||||
server = popts->server;
|
||||
server = popts->has_server ? popts->server : NULL;
|
||||
|
||||
g->dev = dev;
|
||||
|
||||
|
915
audio/pwaudio.c
915
audio/pwaudio.c
@@ -1,915 +0,0 @@
|
||||
/*
|
||||
* QEMU Pipewire audio driver
|
||||
*
|
||||
* Copyright (c) 2023 Red Hat Inc.
|
||||
*
|
||||
* Author: Dorinda Bassey <dbassey@redhat.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "audio.h"
|
||||
#include <errno.h>
|
||||
#include "qemu/error-report.h"
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
#include <spa/utils/ringbuffer.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/param/props.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include "trace.h"
|
||||
|
||||
#define AUDIO_CAP "pipewire"
|
||||
#define RINGBUFFER_SIZE (1u << 22)
|
||||
#define RINGBUFFER_MASK (RINGBUFFER_SIZE - 1)
|
||||
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct pwvolume {
|
||||
uint32_t channels;
|
||||
float values[SPA_AUDIO_MAX_CHANNELS];
|
||||
} pwvolume;
|
||||
|
||||
typedef struct pwaudio {
|
||||
Audiodev *dev;
|
||||
struct pw_thread_loop *thread_loop;
|
||||
struct pw_context *context;
|
||||
|
||||
struct pw_core *core;
|
||||
struct spa_hook core_listener;
|
||||
int last_seq, pending_seq, error;
|
||||
} pwaudio;
|
||||
|
||||
typedef struct PWVoice {
|
||||
pwaudio *g;
|
||||
struct pw_stream *stream;
|
||||
struct spa_hook stream_listener;
|
||||
struct spa_audio_info_raw info;
|
||||
uint32_t highwater_mark;
|
||||
uint32_t frame_size, req;
|
||||
struct spa_ringbuffer ring;
|
||||
uint8_t buffer[RINGBUFFER_SIZE];
|
||||
|
||||
pwvolume volume;
|
||||
bool muted;
|
||||
} PWVoice;
|
||||
|
||||
typedef struct PWVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
PWVoice v;
|
||||
} PWVoiceOut;
|
||||
|
||||
typedef struct PWVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
PWVoice v;
|
||||
} PWVoiceIn;
|
||||
|
||||
static void
|
||||
stream_destroy(void *data)
|
||||
{
|
||||
PWVoice *v = (PWVoice *) data;
|
||||
spa_hook_remove(&v->stream_listener);
|
||||
v->stream = NULL;
|
||||
}
|
||||
|
||||
/* output data processing function to read stuffs from the buffer */
|
||||
static void
|
||||
playback_on_process(void *data)
|
||||
{
|
||||
PWVoice *v = data;
|
||||
void *p;
|
||||
struct pw_buffer *b;
|
||||
struct spa_buffer *buf;
|
||||
uint32_t req, index, n_bytes;
|
||||
int32_t avail;
|
||||
|
||||
assert(v->stream);
|
||||
|
||||
/* obtain a buffer to read from */
|
||||
b = pw_stream_dequeue_buffer(v->stream);
|
||||
if (b == NULL) {
|
||||
error_report("out of buffers: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
buf = b->buffer;
|
||||
p = buf->datas[0].data;
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
/* calculate the total no of bytes to read data from buffer */
|
||||
req = b->requested * v->frame_size;
|
||||
if (req == 0) {
|
||||
req = v->req;
|
||||
}
|
||||
n_bytes = SPA_MIN(req, buf->datas[0].maxsize);
|
||||
|
||||
/* get no of available bytes to read data from buffer */
|
||||
avail = spa_ringbuffer_get_read_index(&v->ring, &index);
|
||||
|
||||
if (avail <= 0) {
|
||||
PWVoiceOut *vo = container_of(data, PWVoiceOut, v);
|
||||
audio_pcm_info_clear_buf(&vo->hw.info, p, n_bytes / v->frame_size);
|
||||
} else {
|
||||
if ((uint32_t) avail < n_bytes) {
|
||||
/*
|
||||
* PipeWire immediately calls this callback again if we provide
|
||||
* less than n_bytes. Then audio_pcm_info_clear_buf() fills the
|
||||
* rest of the buffer with silence.
|
||||
*/
|
||||
n_bytes = avail;
|
||||
}
|
||||
|
||||
spa_ringbuffer_read_data(&v->ring,
|
||||
v->buffer, RINGBUFFER_SIZE,
|
||||
index & RINGBUFFER_MASK, p, n_bytes);
|
||||
|
||||
index += n_bytes;
|
||||
spa_ringbuffer_read_update(&v->ring, index);
|
||||
|
||||
}
|
||||
buf->datas[0].chunk->offset = 0;
|
||||
buf->datas[0].chunk->stride = v->frame_size;
|
||||
buf->datas[0].chunk->size = n_bytes;
|
||||
|
||||
/* queue the buffer for playback */
|
||||
pw_stream_queue_buffer(v->stream, b);
|
||||
}
|
||||
|
||||
/* output data processing function to generate stuffs in the buffer */
|
||||
static void
|
||||
capture_on_process(void *data)
|
||||
{
|
||||
PWVoice *v = (PWVoice *) data;
|
||||
void *p;
|
||||
struct pw_buffer *b;
|
||||
struct spa_buffer *buf;
|
||||
int32_t filled;
|
||||
uint32_t index, offs, n_bytes;
|
||||
|
||||
assert(v->stream);
|
||||
|
||||
/* obtain a buffer */
|
||||
b = pw_stream_dequeue_buffer(v->stream);
|
||||
if (b == NULL) {
|
||||
error_report("out of buffers: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write data into buffer */
|
||||
buf = b->buffer;
|
||||
p = buf->datas[0].data;
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
offs = SPA_MIN(buf->datas[0].chunk->offset, buf->datas[0].maxsize);
|
||||
n_bytes = SPA_MIN(buf->datas[0].chunk->size, buf->datas[0].maxsize - offs);
|
||||
|
||||
filled = spa_ringbuffer_get_write_index(&v->ring, &index);
|
||||
|
||||
|
||||
if (filled < 0) {
|
||||
error_report("%p: underrun write:%u filled:%d", p, index, filled);
|
||||
} else {
|
||||
if ((uint32_t) filled + n_bytes > RINGBUFFER_SIZE) {
|
||||
error_report("%p: overrun write:%u filled:%d + size:%u > max:%u",
|
||||
p, index, filled, n_bytes, RINGBUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
spa_ringbuffer_write_data(&v->ring,
|
||||
v->buffer, RINGBUFFER_SIZE,
|
||||
index & RINGBUFFER_MASK,
|
||||
SPA_PTROFF(p, offs, void), n_bytes);
|
||||
index += n_bytes;
|
||||
spa_ringbuffer_write_update(&v->ring, index);
|
||||
|
||||
/* queue the buffer for playback */
|
||||
pw_stream_queue_buffer(v->stream, b);
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_state_changed(void *data, enum pw_stream_state old,
|
||||
enum pw_stream_state state, const char *error)
|
||||
{
|
||||
PWVoice *v = (PWVoice *) data;
|
||||
|
||||
trace_pw_state_changed(pw_stream_get_node_id(v->stream),
|
||||
pw_stream_state_as_string(state));
|
||||
|
||||
switch (state) {
|
||||
case PW_STREAM_STATE_ERROR:
|
||||
case PW_STREAM_STATE_UNCONNECTED:
|
||||
break;
|
||||
case PW_STREAM_STATE_PAUSED:
|
||||
case PW_STREAM_STATE_CONNECTING:
|
||||
case PW_STREAM_STATE_STREAMING:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pw_stream_events capture_stream_events = {
|
||||
PW_VERSION_STREAM_EVENTS,
|
||||
.destroy = stream_destroy,
|
||||
.state_changed = on_stream_state_changed,
|
||||
.process = capture_on_process
|
||||
};
|
||||
|
||||
static const struct pw_stream_events playback_stream_events = {
|
||||
PW_VERSION_STREAM_EVENTS,
|
||||
.destroy = stream_destroy,
|
||||
.state_changed = on_stream_state_changed,
|
||||
.process = playback_on_process
|
||||
};
|
||||
|
||||
static size_t
|
||||
qpw_read(HWVoiceIn *hw, void *data, size_t len)
|
||||
{
|
||||
PWVoiceIn *pw = (PWVoiceIn *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
pwaudio *c = v->g;
|
||||
const char *error = NULL;
|
||||
size_t l;
|
||||
int32_t avail;
|
||||
uint32_t index;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) {
|
||||
/* wait for stream to become ready */
|
||||
l = 0;
|
||||
goto done_unlock;
|
||||
}
|
||||
/* get no of available bytes to read data from buffer */
|
||||
avail = spa_ringbuffer_get_read_index(&v->ring, &index);
|
||||
|
||||
trace_pw_read(avail, index, len);
|
||||
|
||||
if (avail < (int32_t) len) {
|
||||
len = avail;
|
||||
}
|
||||
|
||||
spa_ringbuffer_read_data(&v->ring,
|
||||
v->buffer, RINGBUFFER_SIZE,
|
||||
index & RINGBUFFER_MASK, data, len);
|
||||
index += len;
|
||||
spa_ringbuffer_read_update(&v->ring, index);
|
||||
l = len;
|
||||
|
||||
done_unlock:
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return l;
|
||||
}
|
||||
|
||||
static size_t qpw_buffer_get_free(HWVoiceOut *hw)
|
||||
{
|
||||
PWVoiceOut *pw = (PWVoiceOut *)hw;
|
||||
PWVoice *v = &pw->v;
|
||||
pwaudio *c = v->g;
|
||||
const char *error = NULL;
|
||||
int32_t filled, avail;
|
||||
uint32_t index;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) {
|
||||
/* wait for stream to become ready */
|
||||
avail = 0;
|
||||
goto done_unlock;
|
||||
}
|
||||
|
||||
filled = spa_ringbuffer_get_write_index(&v->ring, &index);
|
||||
avail = v->highwater_mark - filled;
|
||||
|
||||
done_unlock:
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return avail;
|
||||
}
|
||||
|
||||
static size_t
|
||||
qpw_write(HWVoiceOut *hw, void *data, size_t len)
|
||||
{
|
||||
PWVoiceOut *pw = (PWVoiceOut *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
pwaudio *c = v->g;
|
||||
const char *error = NULL;
|
||||
int32_t filled, avail;
|
||||
uint32_t index;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) {
|
||||
/* wait for stream to become ready */
|
||||
len = 0;
|
||||
goto done_unlock;
|
||||
}
|
||||
filled = spa_ringbuffer_get_write_index(&v->ring, &index);
|
||||
avail = v->highwater_mark - filled;
|
||||
|
||||
trace_pw_write(filled, avail, index, len);
|
||||
|
||||
if (len > avail) {
|
||||
len = avail;
|
||||
}
|
||||
|
||||
if (filled < 0) {
|
||||
error_report("%p: underrun write:%u filled:%d", pw, index, filled);
|
||||
} else {
|
||||
if ((uint32_t) filled + len > RINGBUFFER_SIZE) {
|
||||
error_report("%p: overrun write:%u filled:%d + size:%zu > max:%u",
|
||||
pw, index, filled, len, RINGBUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
spa_ringbuffer_write_data(&v->ring,
|
||||
v->buffer, RINGBUFFER_SIZE,
|
||||
index & RINGBUFFER_MASK, data, len);
|
||||
index += len;
|
||||
spa_ringbuffer_write_update(&v->ring, index);
|
||||
|
||||
done_unlock:
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
audfmt_to_pw(AudioFormat fmt, int endianness)
|
||||
{
|
||||
int format;
|
||||
|
||||
switch (fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
format = SPA_AUDIO_FORMAT_S8;
|
||||
break;
|
||||
case AUDIO_FORMAT_U8:
|
||||
format = SPA_AUDIO_FORMAT_U8;
|
||||
break;
|
||||
case AUDIO_FORMAT_S16:
|
||||
format = endianness ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE;
|
||||
break;
|
||||
case AUDIO_FORMAT_U16:
|
||||
format = endianness ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE;
|
||||
break;
|
||||
case AUDIO_FORMAT_S32:
|
||||
format = endianness ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE;
|
||||
break;
|
||||
case AUDIO_FORMAT_U32:
|
||||
format = endianness ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE;
|
||||
break;
|
||||
case AUDIO_FORMAT_F32:
|
||||
format = endianness ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE;
|
||||
break;
|
||||
default:
|
||||
dolog("Internal logic error: Bad audio format %d\n", fmt);
|
||||
format = SPA_AUDIO_FORMAT_U8;
|
||||
break;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
static AudioFormat
|
||||
pw_to_audfmt(enum spa_audio_format fmt, int *endianness,
|
||||
uint32_t *sample_size)
|
||||
{
|
||||
switch (fmt) {
|
||||
case SPA_AUDIO_FORMAT_S8:
|
||||
*sample_size = 1;
|
||||
return AUDIO_FORMAT_S8;
|
||||
case SPA_AUDIO_FORMAT_U8:
|
||||
*sample_size = 1;
|
||||
return AUDIO_FORMAT_U8;
|
||||
case SPA_AUDIO_FORMAT_S16_BE:
|
||||
*sample_size = 2;
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_S16;
|
||||
case SPA_AUDIO_FORMAT_S16_LE:
|
||||
*sample_size = 2;
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_S16;
|
||||
case SPA_AUDIO_FORMAT_U16_BE:
|
||||
*sample_size = 2;
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_U16;
|
||||
case SPA_AUDIO_FORMAT_U16_LE:
|
||||
*sample_size = 2;
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_U16;
|
||||
case SPA_AUDIO_FORMAT_S32_BE:
|
||||
*sample_size = 4;
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_S32;
|
||||
case SPA_AUDIO_FORMAT_S32_LE:
|
||||
*sample_size = 4;
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_S32;
|
||||
case SPA_AUDIO_FORMAT_U32_BE:
|
||||
*sample_size = 4;
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_U32;
|
||||
case SPA_AUDIO_FORMAT_U32_LE:
|
||||
*sample_size = 4;
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_U32;
|
||||
case SPA_AUDIO_FORMAT_F32_BE:
|
||||
*sample_size = 4;
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_F32;
|
||||
case SPA_AUDIO_FORMAT_F32_LE:
|
||||
*sample_size = 4;
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_F32;
|
||||
default:
|
||||
*sample_size = 1;
|
||||
dolog("Internal logic error: Bad spa_audio_format %d\n", fmt);
|
||||
return AUDIO_FORMAT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
create_stream(pwaudio *c, PWVoice *v, const char *stream_name,
|
||||
const char *name, enum spa_direction dir)
|
||||
{
|
||||
int res;
|
||||
uint32_t n_params;
|
||||
const struct spa_pod *params[2];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b;
|
||||
uint64_t buf_samples;
|
||||
struct pw_properties *props;
|
||||
|
||||
props = pw_properties_new(NULL, NULL);
|
||||
|
||||
/* 75% of the timer period for faster updates */
|
||||
buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate
|
||||
* 3 / 4 / 1000000;
|
||||
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%" PRIu64 "/%u",
|
||||
buf_samples, v->info.rate);
|
||||
|
||||
trace_pw_period(buf_samples, v->info.rate);
|
||||
if (name) {
|
||||
pw_properties_set(props, PW_KEY_TARGET_OBJECT, name);
|
||||
}
|
||||
v->stream = pw_stream_new(c->core, stream_name, props);
|
||||
|
||||
if (v->stream == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dir == SPA_DIRECTION_INPUT) {
|
||||
pw_stream_add_listener(v->stream,
|
||||
&v->stream_listener, &capture_stream_events, v);
|
||||
} else {
|
||||
pw_stream_add_listener(v->stream,
|
||||
&v->stream_listener, &playback_stream_events, v);
|
||||
}
|
||||
|
||||
n_params = 0;
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
params[n_params++] = spa_format_audio_raw_build(&b,
|
||||
SPA_PARAM_EnumFormat,
|
||||
&v->info);
|
||||
|
||||
/* connect the stream to a sink or source */
|
||||
res = pw_stream_connect(v->stream,
|
||||
dir ==
|
||||
SPA_DIRECTION_INPUT ? PW_DIRECTION_INPUT :
|
||||
PW_DIRECTION_OUTPUT, PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_INACTIVE |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||
PW_STREAM_FLAG_RT_PROCESS, params, n_params);
|
||||
if (res < 0) {
|
||||
pw_stream_destroy(v->stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
|
||||
const char *name, enum spa_direction dir)
|
||||
{
|
||||
int r;
|
||||
|
||||
switch (v->info.channels) {
|
||||
case 8:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
|
||||
v->info.position[3] = SPA_AUDIO_CHANNEL_LFE;
|
||||
v->info.position[4] = SPA_AUDIO_CHANNEL_RL;
|
||||
v->info.position[5] = SPA_AUDIO_CHANNEL_RR;
|
||||
v->info.position[6] = SPA_AUDIO_CHANNEL_SL;
|
||||
v->info.position[7] = SPA_AUDIO_CHANNEL_SR;
|
||||
break;
|
||||
case 6:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
|
||||
v->info.position[3] = SPA_AUDIO_CHANNEL_LFE;
|
||||
v->info.position[4] = SPA_AUDIO_CHANNEL_RL;
|
||||
v->info.position[5] = SPA_AUDIO_CHANNEL_RR;
|
||||
break;
|
||||
case 5:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
|
||||
v->info.position[3] = SPA_AUDIO_CHANNEL_LFE;
|
||||
v->info.position[4] = SPA_AUDIO_CHANNEL_RC;
|
||||
break;
|
||||
case 4:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
|
||||
v->info.position[3] = SPA_AUDIO_CHANNEL_RC;
|
||||
break;
|
||||
case 3:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
v->info.position[2] = SPA_AUDIO_CHANNEL_LFE;
|
||||
break;
|
||||
case 2:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
break;
|
||||
case 1:
|
||||
v->info.position[0] = SPA_AUDIO_CHANNEL_MONO;
|
||||
break;
|
||||
default:
|
||||
for (size_t i = 0; i < v->info.channels; i++) {
|
||||
v->info.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* create a new unconnected pwstream */
|
||||
r = create_stream(c, v, stream_name, name, dir);
|
||||
if (r < 0) {
|
||||
AUD_log(AUDIO_CAP, "Failed to create stream.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
PWVoiceOut *pw = (PWVoiceOut *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
struct audsettings obt_as = *as;
|
||||
pwaudio *c = v->g = drv_opaque;
|
||||
AudiodevPipewireOptions *popts = &c->dev->u.pipewire;
|
||||
AudiodevPipewirePerDirectionOptions *ppdo = popts->out;
|
||||
int r;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
|
||||
v->info.format = audfmt_to_pw(as->fmt, as->endianness);
|
||||
v->info.channels = as->nchannels;
|
||||
v->info.rate = as->freq;
|
||||
|
||||
obt_as.fmt =
|
||||
pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size);
|
||||
v->frame_size *= as->nchannels;
|
||||
|
||||
v->req = (uint64_t)c->dev->timer_period * v->info.rate
|
||||
* 1 / 2 / 1000000 * v->frame_size;
|
||||
|
||||
/* call the function that creates a new stream for playback */
|
||||
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
|
||||
ppdo->name, SPA_DIRECTION_OUTPUT);
|
||||
if (r < 0) {
|
||||
error_report("qpw_stream_new for playback failed");
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* report the audio format we support */
|
||||
audio_pcm_init_info(&hw->info, &obt_as);
|
||||
|
||||
/* report the buffer size to qemu */
|
||||
hw->samples = audio_buffer_frames(
|
||||
qapi_AudiodevPipewirePerDirectionOptions_base(ppdo), &obt_as, 46440);
|
||||
v->highwater_mark = MIN(RINGBUFFER_SIZE,
|
||||
(ppdo->has_latency ? ppdo->latency : 46440)
|
||||
* (uint64_t)v->info.rate / 1000000 * v->frame_size);
|
||||
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
PWVoiceIn *pw = (PWVoiceIn *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
struct audsettings obt_as = *as;
|
||||
pwaudio *c = v->g = drv_opaque;
|
||||
AudiodevPipewireOptions *popts = &c->dev->u.pipewire;
|
||||
AudiodevPipewirePerDirectionOptions *ppdo = popts->in;
|
||||
int r;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
|
||||
v->info.format = audfmt_to_pw(as->fmt, as->endianness);
|
||||
v->info.channels = as->nchannels;
|
||||
v->info.rate = as->freq;
|
||||
|
||||
obt_as.fmt =
|
||||
pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size);
|
||||
v->frame_size *= as->nchannels;
|
||||
|
||||
/* call the function that creates a new stream for recording */
|
||||
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
|
||||
ppdo->name, SPA_DIRECTION_INPUT);
|
||||
if (r < 0) {
|
||||
error_report("qpw_stream_new for recording failed");
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* report the audio format we support */
|
||||
audio_pcm_init_info(&hw->info, &obt_as);
|
||||
|
||||
/* report the buffer size to qemu */
|
||||
hw->samples = audio_buffer_frames(
|
||||
qapi_AudiodevPipewirePerDirectionOptions_base(ppdo), &obt_as, 46440);
|
||||
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_fini_out(HWVoiceOut *hw)
|
||||
{
|
||||
PWVoiceOut *pw = (PWVoiceOut *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
|
||||
if (v->stream) {
|
||||
pwaudio *c = v->g;
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
pw_stream_destroy(v->stream);
|
||||
v->stream = NULL;
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_fini_in(HWVoiceIn *hw)
|
||||
{
|
||||
PWVoiceIn *pw = (PWVoiceIn *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
|
||||
if (v->stream) {
|
||||
pwaudio *c = v->g;
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
pw_stream_destroy(v->stream);
|
||||
v->stream = NULL;
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
PWVoiceOut *po = (PWVoiceOut *) hw;
|
||||
PWVoice *v = &po->v;
|
||||
pwaudio *c = v->g;
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
pw_stream_set_active(v->stream, enable);
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
PWVoiceIn *pi = (PWVoiceIn *) hw;
|
||||
PWVoice *v = &pi->v;
|
||||
pwaudio *c = v->g;
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
pw_stream_set_active(v->stream, enable);
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_volume_out(HWVoiceOut *hw, Volume *vol)
|
||||
{
|
||||
PWVoiceOut *pw = (PWVoiceOut *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
pwaudio *c = v->g;
|
||||
int i, ret;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
v->volume.channels = vol->channels;
|
||||
|
||||
for (i = 0; i < vol->channels; ++i) {
|
||||
v->volume.values[i] = (float)vol->vol[i] / 255;
|
||||
}
|
||||
|
||||
ret = pw_stream_set_control(v->stream,
|
||||
SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0);
|
||||
trace_pw_vol(ret == 0 ? "success" : "failed");
|
||||
|
||||
v->muted = vol->mute;
|
||||
float val = v->muted ? 1.f : 0.f;
|
||||
ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0);
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_volume_in(HWVoiceIn *hw, Volume *vol)
|
||||
{
|
||||
PWVoiceIn *pw = (PWVoiceIn *) hw;
|
||||
PWVoice *v = &pw->v;
|
||||
pwaudio *c = v->g;
|
||||
int i, ret;
|
||||
|
||||
pw_thread_loop_lock(c->thread_loop);
|
||||
v->volume.channels = vol->channels;
|
||||
|
||||
for (i = 0; i < vol->channels; ++i) {
|
||||
v->volume.values[i] = (float)vol->vol[i] / 255;
|
||||
}
|
||||
|
||||
ret = pw_stream_set_control(v->stream,
|
||||
SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0);
|
||||
trace_pw_vol(ret == 0 ? "success" : "failed");
|
||||
|
||||
v->muted = vol->mute;
|
||||
float val = v->muted ? 1.f : 0.f;
|
||||
ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0);
|
||||
pw_thread_loop_unlock(c->thread_loop);
|
||||
}
|
||||
|
||||
static int wait_resync(pwaudio *pw)
|
||||
{
|
||||
int res;
|
||||
pw->pending_seq = pw_core_sync(pw->core, PW_ID_CORE, pw->pending_seq);
|
||||
|
||||
while (true) {
|
||||
pw_thread_loop_wait(pw->thread_loop);
|
||||
|
||||
res = pw->error;
|
||||
if (res < 0) {
|
||||
pw->error = 0;
|
||||
return res;
|
||||
}
|
||||
if (pw->pending_seq == pw->last_seq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static void
|
||||
on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
|
||||
{
|
||||
pwaudio *pw = data;
|
||||
|
||||
error_report("error id:%u seq:%d res:%d (%s): %s",
|
||||
id, seq, res, spa_strerror(res), message);
|
||||
|
||||
/* stop and exit the thread loop */
|
||||
pw_thread_loop_signal(pw->thread_loop, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
on_core_done(void *data, uint32_t id, int seq)
|
||||
{
|
||||
pwaudio *pw = data;
|
||||
assert(id == PW_ID_CORE);
|
||||
pw->last_seq = seq;
|
||||
if (pw->pending_seq == seq) {
|
||||
/* stop and exit the thread loop */
|
||||
pw_thread_loop_signal(pw->thread_loop, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pw_core_events core_events = {
|
||||
PW_VERSION_CORE_EVENTS,
|
||||
.done = on_core_done,
|
||||
.error = on_core_error,
|
||||
};
|
||||
|
||||
static void *
|
||||
qpw_audio_init(Audiodev *dev)
|
||||
{
|
||||
g_autofree pwaudio *pw = g_new0(pwaudio, 1);
|
||||
pw_init(NULL, NULL);
|
||||
|
||||
trace_pw_audio_init();
|
||||
assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
|
||||
|
||||
pw->dev = dev;
|
||||
pw->thread_loop = pw_thread_loop_new("Pipewire thread loop", NULL);
|
||||
if (pw->thread_loop == NULL) {
|
||||
error_report("Could not create Pipewire loop");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pw->context =
|
||||
pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0);
|
||||
if (pw->context == NULL) {
|
||||
error_report("Could not create Pipewire context");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pw_thread_loop_start(pw->thread_loop) < 0) {
|
||||
error_report("Could not start Pipewire loop");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pw_thread_loop_lock(pw->thread_loop);
|
||||
|
||||
pw->core = pw_context_connect(pw->context, NULL, 0);
|
||||
if (pw->core == NULL) {
|
||||
pw_thread_loop_unlock(pw->thread_loop);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pw_core_add_listener(pw->core, &pw->core_listener,
|
||||
&core_events, pw) < 0) {
|
||||
pw_thread_loop_unlock(pw->thread_loop);
|
||||
goto fail;
|
||||
}
|
||||
if (wait_resync(pw) < 0) {
|
||||
pw_thread_loop_unlock(pw->thread_loop);
|
||||
}
|
||||
|
||||
pw_thread_loop_unlock(pw->thread_loop);
|
||||
|
||||
return g_steal_pointer(&pw);
|
||||
|
||||
fail:
|
||||
AUD_log(AUDIO_CAP, "Failed to initialize PW context");
|
||||
if (pw->thread_loop) {
|
||||
pw_thread_loop_stop(pw->thread_loop);
|
||||
}
|
||||
if (pw->context) {
|
||||
g_clear_pointer(&pw->context, pw_context_destroy);
|
||||
}
|
||||
if (pw->thread_loop) {
|
||||
g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
qpw_audio_fini(void *opaque)
|
||||
{
|
||||
pwaudio *pw = opaque;
|
||||
|
||||
if (pw->thread_loop) {
|
||||
pw_thread_loop_stop(pw->thread_loop);
|
||||
}
|
||||
|
||||
if (pw->core) {
|
||||
spa_hook_remove(&pw->core_listener);
|
||||
spa_zero(pw->core_listener);
|
||||
pw_core_disconnect(pw->core);
|
||||
}
|
||||
|
||||
if (pw->context) {
|
||||
pw_context_destroy(pw->context);
|
||||
}
|
||||
pw_thread_loop_destroy(pw->thread_loop);
|
||||
|
||||
g_free(pw);
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops qpw_pcm_ops = {
|
||||
.init_out = qpw_init_out,
|
||||
.fini_out = qpw_fini_out,
|
||||
.write = qpw_write,
|
||||
.buffer_get_free = qpw_buffer_get_free,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = qpw_enable_out,
|
||||
.volume_out = qpw_volume_out,
|
||||
.volume_in = qpw_volume_in,
|
||||
|
||||
.init_in = qpw_init_in,
|
||||
.fini_in = qpw_fini_in,
|
||||
.read = qpw_read,
|
||||
.run_buffer_in = audio_generic_run_buffer_in,
|
||||
.enable_in = qpw_enable_in
|
||||
};
|
||||
|
||||
static struct audio_driver pw_audio_driver = {
|
||||
.name = "pipewire",
|
||||
.descr = "http://www.pipewire.org/",
|
||||
.init = qpw_audio_init,
|
||||
.fini = qpw_audio_fini,
|
||||
.pcm_ops = &qpw_pcm_ops,
|
||||
.can_be_default = 1,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof(PWVoiceOut),
|
||||
.voice_size_in = sizeof(PWVoiceIn),
|
||||
};
|
||||
|
||||
static void
|
||||
register_audio_pw(void)
|
||||
{
|
||||
audio_driver_register(&pw_audio_driver);
|
||||
}
|
||||
|
||||
type_init(register_audio_pw);
|
@@ -40,6 +40,8 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
|
||||
int64_t t;
|
||||
#endif
|
||||
|
||||
ilast = rate->ilast;
|
||||
|
||||
istart = ibuf;
|
||||
iend = ibuf + *isamp;
|
||||
|
||||
@@ -57,17 +59,15 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
|
||||
return;
|
||||
}
|
||||
|
||||
/* without input samples, there's nothing to do */
|
||||
if (ibuf >= iend) {
|
||||
*osamp = 0;
|
||||
return;
|
||||
}
|
||||
while (obuf < oend) {
|
||||
|
||||
ilast = rate->ilast;
|
||||
|
||||
while (true) {
|
||||
/* Safety catch to make sure we have input samples. */
|
||||
if (ibuf >= iend) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* read as many input samples so that ipos > opos */
|
||||
|
||||
while (rate->ipos <= (rate->opos >> 32)) {
|
||||
ilast = *ibuf++;
|
||||
rate->ipos++;
|
||||
@@ -78,11 +78,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure that the next output sample can be written */
|
||||
if (obuf >= oend) {
|
||||
break;
|
||||
}
|
||||
|
||||
icur = *ibuf;
|
||||
|
||||
/* wrap ipos and opos around long before they overflow */
|
||||
|
@@ -14,9 +14,9 @@
|
||||
* to recording, which is what guest systems expect.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <poll.h>
|
||||
#include <sndio.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "audio.h"
|
||||
#include "trace.h"
|
||||
@@ -333,7 +333,7 @@ static int sndio_init(SndioVoice *self,
|
||||
unsigned int nch;
|
||||
int i, nfds;
|
||||
|
||||
dev_name = opts->dev ?: SIO_DEVANY;
|
||||
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 */
|
||||
|
@@ -18,14 +18,6 @@ dbus_audio_register(const char *s, const char *dir) "sender = %s, dir = %s"
|
||||
dbus_audio_put_buffer_out(size_t len) "len = %zu"
|
||||
dbus_audio_read(size_t len) "len = %zu"
|
||||
|
||||
# pwaudio.c
|
||||
pw_state_changed(int nodeid, const char *s) "node id: %d stream state: %s"
|
||||
pw_read(int32_t avail, uint32_t index, size_t len) "avail=%d index=%u len=%zu"
|
||||
pw_write(int32_t filled, int32_t avail, uint32_t index, size_t len) "filled=%d avail=%d index=%u len=%zu"
|
||||
pw_vol(const char *ret) "set volume: %s"
|
||||
pw_period(uint64_t quantum, uint32_t rate) "period =%" PRIu64 "/%u"
|
||||
pw_audio_init(void) "Initialize Pipewire context"
|
||||
|
||||
# audio.c
|
||||
audio_timer_start(int interval) "interval %d ms"
|
||||
audio_timer_stop(void) ""
|
||||
|
@@ -78,7 +78,7 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
Audiodev *dev = drv_opaque;
|
||||
AudiodevWavOptions *wopts = &dev->u.wav;
|
||||
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
|
||||
const char *wav_path = wopts->path ?: "qemu.wav";
|
||||
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
|
||||
|
||||
stereo = wav_as.nchannels == 2;
|
||||
switch (wav_as.fmt) {
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "qapi/qapi-visit-authz.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qobject.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
|
||||
|
||||
|
@@ -59,19 +59,6 @@ struct CryptoDevBackendBuiltin {
|
||||
CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS];
|
||||
};
|
||||
|
||||
static void cryptodev_builtin_init_akcipher(CryptoDevBackend *backend)
|
||||
{
|
||||
QCryptoAkCipherOptions opts;
|
||||
|
||||
opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
|
||||
opts.u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
|
||||
if (qcrypto_akcipher_supports(&opts)) {
|
||||
backend->conf.crypto_services |=
|
||||
(1u << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER);
|
||||
backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
|
||||
}
|
||||
}
|
||||
|
||||
static void cryptodev_builtin_init(
|
||||
CryptoDevBackend *backend, Error **errp)
|
||||
{
|
||||
@@ -85,18 +72,21 @@ static void cryptodev_builtin_init(
|
||||
return;
|
||||
}
|
||||
|
||||
cc = cryptodev_backend_new_client();
|
||||
cc = cryptodev_backend_new_client(
|
||||
"cryptodev-builtin", NULL);
|
||||
cc->info_str = g_strdup_printf("cryptodev-builtin0");
|
||||
cc->queue_index = 0;
|
||||
cc->type = QCRYPTODEV_BACKEND_TYPE_BUILTIN;
|
||||
cc->type = CRYPTODEV_BACKEND_TYPE_BUILTIN;
|
||||
backend->conf.peers.ccs[0] = cc;
|
||||
|
||||
backend->conf.crypto_services =
|
||||
1u << QCRYPTODEV_BACKEND_SERVICE_CIPHER |
|
||||
1u << QCRYPTODEV_BACKEND_SERVICE_HASH |
|
||||
1u << QCRYPTODEV_BACKEND_SERVICE_MAC;
|
||||
1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
|
||||
1u << VIRTIO_CRYPTO_SERVICE_HASH |
|
||||
1u << VIRTIO_CRYPTO_SERVICE_MAC |
|
||||
1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
|
||||
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
|
||||
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
|
||||
backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
|
||||
/*
|
||||
* Set the Maximum length of crypto request.
|
||||
* Why this value? Just avoid to overflow when
|
||||
@@ -105,7 +95,6 @@ static void cryptodev_builtin_init(
|
||||
backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
|
||||
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
|
||||
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
|
||||
cryptodev_builtin_init_akcipher(backend);
|
||||
|
||||
cryptodev_backend_set_ready(backend, true);
|
||||
}
|
||||
@@ -539,14 +528,17 @@ static int cryptodev_builtin_asym_operation(
|
||||
|
||||
static int cryptodev_builtin_operation(
|
||||
CryptoDevBackend *backend,
|
||||
CryptoDevBackendOpInfo *op_info)
|
||||
CryptoDevBackendOpInfo *op_info,
|
||||
uint32_t queue_index,
|
||||
CryptoDevCompletionFunc cb,
|
||||
void *opaque)
|
||||
{
|
||||
CryptoDevBackendBuiltin *builtin =
|
||||
CRYPTODEV_BACKEND_BUILTIN(backend);
|
||||
CryptoDevBackendBuiltinSession *sess;
|
||||
CryptoDevBackendSymOpInfo *sym_op_info;
|
||||
CryptoDevBackendAsymOpInfo *asym_op_info;
|
||||
QCryptodevBackendAlgType algtype = op_info->algtype;
|
||||
enum CryptoDevBackendAlgType algtype = op_info->algtype;
|
||||
int status = -VIRTIO_CRYPTO_ERR;
|
||||
Error *local_error = NULL;
|
||||
|
||||
@@ -558,11 +550,11 @@ static int cryptodev_builtin_operation(
|
||||
}
|
||||
|
||||
sess = builtin->sessions[op_info->session_id];
|
||||
if (algtype == QCRYPTODEV_BACKEND_ALG_SYM) {
|
||||
if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
|
||||
sym_op_info = op_info->u.sym_op_info;
|
||||
status = cryptodev_builtin_sym_operation(sess, sym_op_info,
|
||||
&local_error);
|
||||
} else if (algtype == QCRYPTODEV_BACKEND_ALG_ASYM) {
|
||||
} else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
|
||||
asym_op_info = op_info->u.asym_op_info;
|
||||
status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
|
||||
asym_op_info, &local_error);
|
||||
@@ -571,8 +563,8 @@ static int cryptodev_builtin_operation(
|
||||
if (local_error) {
|
||||
error_report_err(local_error);
|
||||
}
|
||||
if (op_info->cb) {
|
||||
op_info->cb(op_info->opaque, status);
|
||||
if (cb) {
|
||||
cb(opaque, status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* HMP commands related to cryptodev
|
||||
*
|
||||
* Copyright (c) 2023 Bytedance.Inc
|
||||
*
|
||||
* Authors:
|
||||
* zhenwei pi<pizhenwei@bytedance.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "monitor/hmp.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/qapi-commands-cryptodev.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
||||
|
||||
void hmp_info_cryptodev(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
QCryptodevInfoList *il;
|
||||
QCryptodevBackendServiceTypeList *sl;
|
||||
QCryptodevBackendClientList *cl;
|
||||
|
||||
for (il = qmp_query_cryptodev(NULL); il; il = il->next) {
|
||||
g_autofree char *services = NULL;
|
||||
QCryptodevInfo *info = il->value;
|
||||
char *tmp_services;
|
||||
|
||||
/* build a string like 'service=[akcipher|mac|hash|cipher]' */
|
||||
for (sl = info->service; sl; sl = sl->next) {
|
||||
const char *service = QCryptodevBackendServiceType_str(sl->value);
|
||||
|
||||
if (!services) {
|
||||
services = g_strdup(service);
|
||||
} else {
|
||||
tmp_services = g_strjoin("|", services, service, NULL);
|
||||
g_free(services);
|
||||
services = tmp_services;
|
||||
}
|
||||
}
|
||||
monitor_printf(mon, "%s: service=[%s]\n", info->id, services);
|
||||
|
||||
for (cl = info->client; cl; cl = cl->next) {
|
||||
QCryptodevBackendClient *client = cl->value;
|
||||
monitor_printf(mon, " queue %" PRIu32 ": type=%s\n",
|
||||
client->queue,
|
||||
QCryptodevBackendType_str(client->type));
|
||||
}
|
||||
}
|
||||
|
||||
qapi_free_QCryptodevInfoList(il);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user