Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99c5874a9b | ||
|
|
e092a17d38 | ||
|
|
088f1e8fd9 | ||
|
|
145b562990 | ||
|
|
42b6571357 | ||
|
|
c0aca9352d | ||
|
|
54c130493c | ||
|
|
3d018ff3bd | ||
|
|
f0d3fa265d | ||
|
|
7db05c8a73 | ||
|
|
d9b88f7e0d | ||
|
|
590cff8230 | ||
|
|
2e2ad02f2c | ||
|
|
b3b76fc643 | ||
|
|
cff024fe85 | ||
|
|
40df4a1bf7 | ||
|
|
b156178553 | ||
|
|
15f5e8c367 | ||
|
|
405deba14f | ||
|
|
01be50603b | ||
|
|
051c9b3cbc | ||
|
|
b387531323 | ||
|
|
cdc6896659 | ||
|
|
c0b35d87de | ||
|
|
fcd7cba6ac | ||
|
|
aea18ef938 | ||
|
|
4887acf574 | ||
|
|
8010d3fce0 | ||
|
|
6705b9344f | ||
|
|
c0e2fbf124 | ||
|
|
b077ac637d | ||
|
|
9e51c5306c | ||
|
|
aae0faa5d3 | ||
|
|
7b404cae7f | ||
|
|
d868d30db6 | ||
|
|
7a8aa6c734 | ||
|
|
331c08d300 | ||
|
|
fc5afb1a92 | ||
|
|
c5c9b1362d | ||
|
|
220816989c | ||
|
|
783e7eb52c | ||
|
|
e01ed1a1ae | ||
|
|
416a692e51 | ||
|
|
e9bb3d942e | ||
|
|
84f22c7285 | ||
|
|
86b0f4022b | ||
|
|
2d86df1f78 | ||
|
|
18e1b71937 | ||
|
|
0888ddac8e | ||
|
|
4be97ef966 | ||
|
|
78ea94e389 | ||
|
|
3648493495 | ||
|
|
0694c489cd | ||
|
|
c9ffb12754 | ||
|
|
28a9a3558a | ||
|
|
9027d3fba6 | ||
|
|
38fb634853 | ||
|
|
ad95e0573e | ||
|
|
593beeaf81 | ||
|
|
eee776fbc0 | ||
|
|
819ba23575 | ||
|
|
4d9bdd3149 | ||
|
|
97c478c355 | ||
|
|
725dfa851f | ||
|
|
57fdf4a13f | ||
|
|
3361d03ff0 | ||
|
|
6f1a94035b | ||
|
|
c12adfd8f6 | ||
|
|
b6cedc911e | ||
|
|
7ebcd375ad | ||
|
|
4bfd496be3 | ||
|
|
499a5d6bb4 | ||
|
|
73a5bf4729 | ||
|
|
fbde196c30 |
No files matched your search
+7
-22
@@ -3,40 +3,25 @@ env:
|
||||
|
||||
freebsd_12_task:
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-1
|
||||
image: freebsd-12-0-release-amd64
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
install_script: ASSUME_ALWAYS_YES=yes pkg bootstrap -f ; pkg install -y
|
||||
bash curl cyrus-sasl git glib gmake gnutls gsed
|
||||
install_script: pkg install -y
|
||||
bash bison curl cyrus-sasl git glib gmake gnutls gsed
|
||||
nettle perl5 pixman pkgconf png usbredir
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure || { cat config.log; exit 1; }
|
||||
- gmake -j8
|
||||
- gmake V=1 check
|
||||
- gmake -j8 V=1 check
|
||||
|
||||
macos_task:
|
||||
osx_instance:
|
||||
image: mojave-base
|
||||
install_script:
|
||||
- brew install pkg-config python gnu-sed glib pixman make sdl2 bash
|
||||
- brew install pkg-config python gnu-sed glib pixman make sdl2
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||
- ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check
|
||||
|
||||
macos_xcode_task:
|
||||
osx_instance:
|
||||
# this is an alias for the latest Xcode
|
||||
image: mojave-xcode
|
||||
install_script:
|
||||
- brew install pkg-config gnu-sed glib pixman make sdl2 bash
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --cc=clang || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check
|
||||
- gmake check -j$(sysctl -n hw.ncpu)
|
||||
@@ -1,34 +0,0 @@
|
||||
# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown
|
||||
|
||||
# Close issues and pull requests
|
||||
close: true
|
||||
|
||||
# Lock issues and pull requests
|
||||
lock: true
|
||||
|
||||
issues:
|
||||
comment: |
|
||||
Thank you for your interest in the QEMU project.
|
||||
|
||||
This repository is a read-only mirror of the project's master
|
||||
repostories hosted on https://git.qemu.org/git/qemu.git.
|
||||
The project does not process issues filed on GitHub.
|
||||
|
||||
The project issues are tracked on Launchpad:
|
||||
https://bugs.launchpad.net/qemu
|
||||
|
||||
QEMU welcomes bug report contributions. You can file new ones on:
|
||||
https://bugs.launchpad.net/qemu/+filebug
|
||||
|
||||
pulls:
|
||||
comment: |
|
||||
Thank you for your interest in the QEMU project.
|
||||
|
||||
This repository is a read-only mirror of the project's master
|
||||
repostories hosted on https://git.qemu.org/git/qemu.git.
|
||||
The project does not process merge requests filed on GitHub.
|
||||
|
||||
QEMU welcomes contributions of code (either fixing bugs or adding new
|
||||
functionality). However, we get a lot of patches, and so we have some
|
||||
guidelines about contributing on the project website:
|
||||
https://www.qemu.org/contribute/
|
||||
+10
-15
@@ -6,7 +6,6 @@
|
||||
/config-target.*
|
||||
/config.status
|
||||
/config-temp
|
||||
/tools/virtiofsd/50-qemu-virtiofsd.json
|
||||
/elf2dmp
|
||||
/trace-events-all
|
||||
/trace/generated-events.h
|
||||
@@ -34,18 +33,19 @@
|
||||
/qapi/qapi-builtin-types.[ch]
|
||||
/qapi/qapi-builtin-visit.[ch]
|
||||
/qapi/qapi-commands-*.[ch]
|
||||
**/qapi/qapi-commands.[ch]
|
||||
**/qapi/qapi-emit-events.[ch]
|
||||
/qapi/qapi-commands.[ch]
|
||||
/qapi/qapi-emit-events.[ch]
|
||||
/qapi/qapi-events-*.[ch]
|
||||
**/qapi/qapi-events.[ch]
|
||||
**/qapi/qapi-init-commands.[ch]
|
||||
**/qapi/qapi-introspect.[ch]
|
||||
/qapi/qapi-events.[ch]
|
||||
/qapi/qapi-introspect.[ch]
|
||||
/qapi/qapi-types-*.[ch]
|
||||
**/qapi/qapi-types.[ch]
|
||||
/qapi/qapi-types.[ch]
|
||||
/qapi/qapi-visit-*.[ch]
|
||||
!/qapi/qapi-visit-core.c
|
||||
**/qapi/qapi-visit.[ch]
|
||||
**/qapi/qapi-doc.texi
|
||||
/qapi/qapi-visit.[ch]
|
||||
/qapi/qapi-doc.texi
|
||||
/qemu-doc.html
|
||||
/qemu-doc.info
|
||||
/qemu-doc.txt
|
||||
/qemu-edid
|
||||
/qemu-img
|
||||
/qemu-nbd
|
||||
@@ -59,15 +59,12 @@
|
||||
/qemu-keymap
|
||||
/qemu-monitor.texi
|
||||
/qemu-monitor-info.texi
|
||||
/qemu-storage-daemon
|
||||
/qemu-version.h
|
||||
/qemu-version.h.tmp
|
||||
/module_block.h
|
||||
/scsi/qemu-pr-helper
|
||||
/vhost-user-scsi
|
||||
/vhost-user-blk
|
||||
/vhost-user-gpu
|
||||
/vhost-user-input
|
||||
/fsdev/virtfs-proxy-helper
|
||||
*.tmp
|
||||
*.[1-9]
|
||||
@@ -93,7 +90,6 @@
|
||||
*.tp
|
||||
*.vr
|
||||
*.d
|
||||
!/.gitlab-ci.d
|
||||
!/scripts/qemu-guest-agent/fsfreeze-hook.d
|
||||
*.o
|
||||
.sdk
|
||||
@@ -135,7 +131,6 @@
|
||||
/docs/interop/qemu-qmp-ref.info*
|
||||
/docs/interop/qemu-qmp-ref.txt
|
||||
/docs/version.texi
|
||||
/contrib/vhost-user-gpu/50-qemu-gpu.json
|
||||
*.tps
|
||||
.stgit-*
|
||||
.git-submodule-status
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
.container_job_template: &container_job_definition
|
||||
image: docker:stable
|
||||
stage: containers
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- 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 registry.gitlab.com -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||
script:
|
||||
- echo "TAG:$TAG"
|
||||
- echo "COMMON_TAG:$COMMON_TAG"
|
||||
- docker pull "$TAG" || docker pull "$COMMON_TAG" || true
|
||||
- ./tests/docker/docker.py --engine docker build
|
||||
-t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
|
||||
-r $CI_REGISTRY_IMAGE
|
||||
- docker tag "qemu/$NAME" "$TAG"
|
||||
- docker push "$TAG"
|
||||
after_script:
|
||||
- docker logout
|
||||
rules:
|
||||
- changes:
|
||||
- .gitlab-ci.d/containers.yml
|
||||
- tests/docker/*
|
||||
- tests/docker/dockerfiles/*
|
||||
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
||||
- if: '$CI_COMMIT_REF_NAME == "testing/next"'
|
||||
|
||||
amd64-centos7-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: centos7
|
||||
|
||||
amd64-centos8-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: centos8
|
||||
|
||||
amd64-debian10-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: debian10
|
||||
|
||||
amd64-debian11-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: debian11
|
||||
|
||||
amd64-debian9-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: debian9
|
||||
|
||||
amd64-debian9-mxe-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian9-container']
|
||||
variables:
|
||||
NAME: debian9-mxe
|
||||
|
||||
alpha-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-alpha-cross
|
||||
|
||||
amd64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-amd64-cross
|
||||
|
||||
amd64-debian-user-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-all-test-cross
|
||||
|
||||
amd64-debian-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-amd64
|
||||
|
||||
arm64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-arm64-cross
|
||||
|
||||
arm64-test-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian11-container']
|
||||
variables:
|
||||
NAME: debian-arm64-test-cross
|
||||
|
||||
armel-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-armel-cross
|
||||
|
||||
armhf-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-armhf-cross
|
||||
|
||||
hppa-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-hppa-cross
|
||||
|
||||
m68k-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-m68k-cross
|
||||
|
||||
mips64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips64-cross
|
||||
|
||||
mips64el-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips64el-cross
|
||||
|
||||
mips-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips-cross
|
||||
|
||||
mipsel-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mipsel-cross
|
||||
|
||||
powerpc-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-powerpc-cross
|
||||
|
||||
ppc64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-ppc64-cross
|
||||
|
||||
ppc64el-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-ppc64el-cross
|
||||
|
||||
riscv64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-riscv64-cross
|
||||
|
||||
s390x-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-s390x-cross
|
||||
|
||||
sh4-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-sh4-cross
|
||||
|
||||
sparc64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-sparc64-cross
|
||||
|
||||
tricore-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian9-container']
|
||||
variables:
|
||||
NAME: debian-tricore-cross
|
||||
|
||||
win32-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer3
|
||||
needs: ['amd64-debian9-mxe-container']
|
||||
variables:
|
||||
NAME: debian-win32-cross
|
||||
|
||||
win64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer3
|
||||
needs: ['amd64-debian9-mxe-container']
|
||||
variables:
|
||||
NAME: debian-win64-cross
|
||||
|
||||
xtensa-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: debian-xtensa-cross
|
||||
|
||||
cris-fedora-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora-cris-cross
|
||||
|
||||
amd64-fedora-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora
|
||||
|
||||
i386-fedora-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora-i386-cross
|
||||
|
||||
amd64-ubuntu1804-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: ubuntu1804
|
||||
|
||||
amd64-ubuntu2004-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: ubuntu2004
|
||||
|
||||
amd64-ubuntu-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: ubuntu
|
||||
@@ -1,50 +0,0 @@
|
||||
docker-edk2:
|
||||
stage: containers
|
||||
rules: # Only run this job when the Dockerfile is modified
|
||||
- changes:
|
||||
- .gitlab-ci.d/edk2.yml
|
||||
- .gitlab-ci.d/edk2/Dockerfile
|
||||
when: always
|
||||
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:
|
||||
stage: build
|
||||
rules: # Only run this job when ...
|
||||
- changes: # ... roms/edk2/ is modified (submodule updated)
|
||||
- roms/edk2/*
|
||||
when: always
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2'
|
||||
when: always
|
||||
artifacts:
|
||||
paths: # 'artifacts.zip' will contains the following files:
|
||||
- pc-bios/edk2*bz2
|
||||
- pc-bios/edk2-licenses.txt
|
||||
- edk2-stdout.log
|
||||
- edk2-stderr.log
|
||||
image: $CI_REGISTRY_IMAGE:edk2-cross-build
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
script: # Clone the required submodules and build EDK2
|
||||
- git submodule update --init roms/edk2
|
||||
- git -C roms/edk2 submodule update --init
|
||||
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
- echo "=== Using ${JOBS} simultaneous jobs ==="
|
||||
- make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2
|
||||
@@ -1,27 +0,0 @@
|
||||
#
|
||||
# Docker image to cross-compile EDK2 firmware binaries
|
||||
#
|
||||
FROM ubuntu:16.04
|
||||
|
||||
MAINTAINER Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
|
||||
# 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 \
|
||||
python \
|
||||
uuid-dev \
|
||||
&& \
|
||||
\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
@@ -1,64 +0,0 @@
|
||||
docker-opensbi:
|
||||
stage: containers
|
||||
rules: # Only run this job when the Dockerfile is modified
|
||||
- changes:
|
||||
- .gitlab-ci.d/opensbi.yml
|
||||
- .gitlab-ci.d/opensbi/Dockerfile
|
||||
when: always
|
||||
image: docker:19.03.1
|
||||
services:
|
||||
- docker:19.03.1-dind
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build
|
||||
# We don't use TLS
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker pull $IMAGE_TAG || true
|
||||
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
--tag $IMAGE_TAG .gitlab-ci.d/opensbi
|
||||
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
- docker push $IMAGE_TAG
|
||||
|
||||
build-opensbi:
|
||||
stage: build
|
||||
rules: # Only run this job when ...
|
||||
- changes: # ... roms/opensbi/ is modified (submodule updated)
|
||||
- roms/opensbi/*
|
||||
when: always
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI'
|
||||
when: always
|
||||
artifacts:
|
||||
paths: # 'artifacts.zip' will contains the following files:
|
||||
- pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
|
||||
- pc-bios/opensbi-riscv32-virt-fw_jump.bin
|
||||
- pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
|
||||
- pc-bios/opensbi-riscv64-virt-fw_jump.bin
|
||||
- opensbi32-virt-stdout.log
|
||||
- opensbi32-virt-stderr.log
|
||||
- opensbi64-virt-stdout.log
|
||||
- opensbi64-virt-stderr.log
|
||||
- opensbi32-sifive_u-stdout.log
|
||||
- opensbi32-sifive_u-stderr.log
|
||||
- opensbi64-sifive_u-stdout.log
|
||||
- opensbi64-sifive_u-stderr.log
|
||||
image: $CI_REGISTRY_IMAGE:opensbi-cross-build
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
script: # Clone the required submodules and build OpenSBI
|
||||
- git submodule update --init roms/opensbi
|
||||
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
- echo "=== Using ${JOBS} simultaneous jobs ==="
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi32-virt 2>&1 1>opensbi32-virt-stdout.log | tee -a opensbi32-virt-stderr.log >&2
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi64-virt 2>&1 1>opensbi64-virt-stdout.log | tee -a opensbi64-virt-stderr.log >&2
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi32-sifive_u 2>&1 1>opensbi32-sifive_u-stdout.log | tee -a opensbi32-sifive_u-stderr.log >&2
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi64-sifive_u 2>&1 1>opensbi64-sifive_u-stdout.log | tee -a opensbi64-sifive_u-stderr.log >&2
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# Docker image to cross-compile OpenSBI firmware binaries
|
||||
#
|
||||
FROM ubuntu:18.04
|
||||
|
||||
MAINTAINER Bin Meng <bmeng.cn@gmail.com>
|
||||
|
||||
# Install packages required to build OpenSBI
|
||||
RUN apt update \
|
||||
&& \
|
||||
\
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
apt install --assume-yes --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
git \
|
||||
make \
|
||||
wget \
|
||||
&& \
|
||||
\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Manually install the kernel.org "Crosstool" based toolchains for gcc-8.3
|
||||
RUN wget -O - \
|
||||
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv32-linux.tar.xz \
|
||||
| tar -C /opt -xJ
|
||||
RUN wget -O - \
|
||||
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv64-linux.tar.xz \
|
||||
| tar -C /opt -xJ
|
||||
|
||||
# Export the toolchains to the system path
|
||||
ENV PATH="/opt/gcc-8.3.0-nolibc/riscv32-linux/bin:${PATH}"
|
||||
ENV PATH="/opt/gcc-8.3.0-nolibc/riscv64-linux/bin:${PATH}"
|
||||
+71
-256
@@ -1,273 +1,88 @@
|
||||
# Currently we have two build stages after our containers are built:
|
||||
# - build (for traditional build and test or first stage build)
|
||||
# - test (for test stages, using build artefacts from a build stage)
|
||||
stages:
|
||||
- containers
|
||||
- containers-layer2
|
||||
- containers-layer3
|
||||
- build
|
||||
- test
|
||||
before_script:
|
||||
- apt-get update -qq
|
||||
- apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
|
||||
|
||||
# We assume GitLab has it's own caching set up for RPM/APT repositories so we
|
||||
# just take care of avocado assets here.
|
||||
cache:
|
||||
paths:
|
||||
- $HOME/avocado/data/cache
|
||||
build-system1:
|
||||
script:
|
||||
- apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
|
||||
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
|
||||
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
|
||||
cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
|
||||
mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/edk2.yml'
|
||||
- local: '/.gitlab-ci.d/opensbi.yml'
|
||||
- local: '/.gitlab-ci.d/containers.yml'
|
||||
|
||||
.native_build_job_template: &native_build_job_definition
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
before_script:
|
||||
- JOBS=$(expr $(nproc) + 1)
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if test -n "$TARGETS";
|
||||
then
|
||||
../configure --enable-werror $CONFIGURE_ARGS --target-list="$TARGETS" ;
|
||||
else
|
||||
../configure --enable-werror $CONFIGURE_ARGS ;
|
||||
fi
|
||||
- make -j"$JOBS"
|
||||
- if test -n "$MAKE_CHECK_ARGS";
|
||||
then
|
||||
make -j"$JOBS" $MAKE_CHECK_ARGS ;
|
||||
fi
|
||||
|
||||
.native_test_job_template: &native_test_job_definition
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- cd build
|
||||
- find . -type f -exec touch {} +
|
||||
- make $MAKE_CHECK_ARGS
|
||||
|
||||
.acceptance_template: &acceptance_definition
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}-cache"
|
||||
paths:
|
||||
- ${CI_PROJECT_DIR}/avocado-cache
|
||||
policy: pull-push
|
||||
before_script:
|
||||
- mkdir -p ~/.config/avocado
|
||||
- echo "[datadir.paths]" > ~/.config/avocado/avocado.conf
|
||||
- echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']"
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then
|
||||
du -chs ${CI_PROJECT_DIR}/avocado-cache ;
|
||||
fi
|
||||
after_script:
|
||||
- cd build
|
||||
- python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat
|
||||
- du -chs ${CI_PROJECT_DIR}/avocado-cache
|
||||
|
||||
build-system-ubuntu:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
moxie-softmmu microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-ubuntu:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-ubuntu:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-debian:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
|
||||
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-debian:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-debian
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-debian:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-debian
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-fedora:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
TARGETS: tricore-softmmu unicore32-softmmu microblaze-softmmu mips-softmmu
|
||||
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-fedora:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-fedora
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-fedora:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-fedora
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-centos:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
TARGETS: ppc64-softmmu lm32-softmmu or1k-softmmu s390x-softmmu
|
||||
x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-centos:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-centos:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
build-system2:
|
||||
script:
|
||||
- apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
|
||||
libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
|
||||
- ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
|
||||
microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
|
||||
sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
build-disabled:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --disable-rdma --disable-slirp --disable-curl
|
||||
script:
|
||||
- ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
|
||||
--disable-capstone --disable-live-block-migration --disable-glusterfs
|
||||
--disable-replication --disable-coroutine-pool --disable-smartcard
|
||||
--disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
|
||||
--disable-qom-cast-debug --disable-spice --disable-vhost-vsock
|
||||
--disable-vhost-net --disable-vhost-crypto --disable-vhost-user
|
||||
TARGETS: i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user
|
||||
MAKE_CHECK_ARGS: check-qtest SPEED=slow
|
||||
--target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
|
||||
- make -j2
|
||||
- make -j2 check-qtest SPEED=slow
|
||||
|
||||
build-tcg-disabled:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-tcg --audio-drv-list=""
|
||||
- make -j"$JOBS"
|
||||
- make check-unit
|
||||
- make check-qapi-schema
|
||||
- cd tests/qemu-iotests/
|
||||
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
|
||||
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
|
||||
- ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122
|
||||
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202
|
||||
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258
|
||||
260 261 262 263 264 270 272 273 277 279
|
||||
script:
|
||||
- apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
|
||||
- ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
|
||||
- make -j2
|
||||
- make check-unit
|
||||
- make check-qapi-schema
|
||||
- cd tests/qemu-iotests/
|
||||
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||
052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
|
||||
163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
|
||||
- ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
|
||||
020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
|
||||
039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
|
||||
060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
|
||||
085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
|
||||
110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
|
||||
139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
|
||||
161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
|
||||
197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
|
||||
|
||||
build-user:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
script:
|
||||
- ./configure --enable-werror --disable-system --disable-guest-agent
|
||||
--disable-capstone --disable-slirp --disable-fdt
|
||||
- make -j2
|
||||
- make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
|
||||
|
||||
build-clang:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++
|
||||
TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||
ppc-softmmu s390x-softmmu arm-linux-user
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
build-oss-fuzz:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
script:
|
||||
- mkdir build-oss-fuzz
|
||||
- CC="clang" CXX="clang++" CFLAGS="-fsanitize=address"
|
||||
./scripts/oss-fuzz/build.sh
|
||||
- for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f
|
||||
| grep -v slirp); do
|
||||
grep "LLVMFuzzerTestOneInput" ${fuzzer} > /dev/null 2>&1 || continue ;
|
||||
echo Testing ${fuzzer} ... ;
|
||||
ASAN_OPTIONS="fast_unwind_on_malloc=0"
|
||||
"${fuzzer}" -runs=1000 -seed=1 || exit 1 ;
|
||||
done
|
||||
script:
|
||||
- apt-get install -y -qq clang libsdl2-dev
|
||||
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
|
||||
- ./configure --cc=clang --cxx=clang++ --enable-werror
|
||||
--target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
build-tci:
|
||||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
||||
- make -j"$JOBS"
|
||||
- make run-tcg-tests-x86_64-softmmu
|
||||
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
|
||||
./tests/qtest/boot-serial-test || exit 1 ;
|
||||
./tests/qtest/cdrom-test || exit 1 ;
|
||||
done
|
||||
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/qtest/pxe-test
|
||||
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/qtest/pxe-test -m slow
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
|
||||
- ./configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
||||
- make -j2
|
||||
- make tests/boot-serial-test tests/cdrom-test tests/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
|
||||
./tests/boot-serial-test || exit 1 ;
|
||||
./tests/cdrom-test || exit 1 ;
|
||||
done
|
||||
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test
|
||||
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow
|
||||
+3
-3
@@ -10,6 +10,9 @@
|
||||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = https://git.qemu.org/git/openbios.git
|
||||
[submodule "roms/openhackware"]
|
||||
path = roms/openhackware
|
||||
url = https://git.qemu.org/git/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = https://git.qemu.org/git/qemu-palcode.git
|
||||
@@ -55,6 +58,3 @@
|
||||
[submodule "roms/opensbi"]
|
||||
path = roms/opensbi
|
||||
url = https://git.qemu.org/git/opensbi.git
|
||||
[submodule "roms/qboot"]
|
||||
path = roms/qboot
|
||||
url = https://github.com/bonzini/qboot
|
||||
@@ -1,29 +1,23 @@
|
||||
# This mailmap fixes up author names/addresses.
|
||||
#
|
||||
# If you are adding to this file consider if a similar change needs to
|
||||
# be made to contrib/gitdm/aliases. They are not however completely
|
||||
# analogous. .mailmap is concerned with fixing up damaged author
|
||||
# fields where as the gitdm equivalent is more concerned with making
|
||||
# sure multiple email addresses get mapped onto the same author.
|
||||
#
|
||||
# From man git-shortlog the forms are:
|
||||
#
|
||||
# Proper Name <commit@email.xx>
|
||||
# <proper@email.xx> <commit@email.xx>
|
||||
# Proper Name <proper@email.xx> <commit@email.xx>
|
||||
# Proper Name <proper@email.xx> Commit Name <commit@email.xx>
|
||||
#
|
||||
|
||||
# The first section translates weird addresses from the original git import
|
||||
# into proper addresses so that they are counted properly by git shortlog.
|
||||
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||
Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
|
||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
|
||||
@@ -38,133 +32,8 @@ Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu
|
||||
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>
|
||||
|
||||
# Next, replace old addresses by a more recent one.
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
|
||||
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
|
||||
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
|
||||
Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
|
||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||
Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com>
|
||||
Frederic Konrad <konrad@adacore.com> <fred.konrad@greensocs.com>
|
||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||
Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org>
|
||||
Radoslaw Biernacki <rad@semihalf.com> <radoslaw.biernacki@linaro.org>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
|
||||
Philippe Mathieu-Daudé <philmd@redhat.com> <f4bug@amsat.org>
|
||||
Stefan Brankovic <stefan.brankovic@syrmia.com> <stefan.brankovic@rt-rk.com.com>
|
||||
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
|
||||
|
||||
# Also list preferred name forms where people have changed their
|
||||
# git author config, or had utf8/latin1 encoding issues.
|
||||
Aaron Lindsay <aaron@os.amperecomputing.com>
|
||||
Alexey Gerasimenko <x1917x@gmail.com>
|
||||
Alex Ivanov <void@aleksoft.net>
|
||||
Andreas Färber <afaerber@suse.de>
|
||||
Bandan Das <bsd@redhat.com>
|
||||
Benjamin MARSILI <mlspirat42@gmail.com>
|
||||
Benoît Canet <benoit.canet@gmail.com>
|
||||
Benoît Canet <benoit.canet@irqsave.net>
|
||||
Benoît Canet <benoit.canet@nodalink.com>
|
||||
Boqun Feng <boqun.feng@gmail.com>
|
||||
Boqun Feng <boqun.feng@intel.com>
|
||||
Brad Smith <brad@comstyle.com>
|
||||
Brijesh Singh <brijesh.singh@amd.com>
|
||||
Brilly Wu <brillywu@viatech.com.cn>
|
||||
Cédric Vincent <cedric.vincent@st.com>
|
||||
CheneyLin <linzc@zju.edu.cn>
|
||||
Chen Gang <chengang@emindsoft.com.cn>
|
||||
Chen Gang <gang.chen.5i5j@gmail.com>
|
||||
Chen Gang <gang.chen@sunrus.com.cn>
|
||||
Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
|
||||
Christophe Lyon <christophe.lyon@st.com>
|
||||
Collin L. Walling <walling@linux.ibm.com>
|
||||
Daniel P. Berrangé <berrange@redhat.com>
|
||||
Eduardo Otubo <otubo@redhat.com>
|
||||
Fabrice Desclaux <fabrice.desclaux@cea.fr>
|
||||
Fernando Luis Vázquez Cao <fernando_b1@lab.ntt.co.jp>
|
||||
Fernando Luis Vázquez Cao <fernando@oss.ntt.co.jp>
|
||||
Gautham R. Shenoy <ego@in.ibm.com>
|
||||
Gautham R. Shenoy <ego@linux.vnet.ibm.com>
|
||||
Gonglei (Arei) <arei.gonglei@huawei.com>
|
||||
Guang Wang <wang.guang55@zte.com.cn>
|
||||
Hailiang Zhang <zhang.zhanghailiang@huawei.com>
|
||||
Hervé Poussineau <hpoussin@reactos.org>
|
||||
Jakub Jermář <jakub@jermar.eu>
|
||||
Jakub Jermář <jakub.jermar@kernkonzept.com>
|
||||
Jean-Christophe Dubois <jcd@tribudubois.net>
|
||||
Jindřich Makovička <makovick@gmail.com>
|
||||
John Arbuckle <programmingkidx@gmail.com>
|
||||
Juha Riihimäki <juha.riihimaki@nokia.com>
|
||||
Juha Riihimäki <Juha.Riihimaki@nokia.com>
|
||||
Jun Li <junmuzi@gmail.com>
|
||||
Laurent Vivier <Laurent@lvivier.info>
|
||||
Leandro Lupori <leandro.lupori@gmail.com>
|
||||
Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
Liming Wang <walimisdev@gmail.com>
|
||||
linzhecheng <linzc@zju.edu.cn>
|
||||
Liran Schour <lirans@il.ibm.com>
|
||||
Liu Yu <yu.liu@freescale.com>
|
||||
Liu Yu <Yu.Liu@freescale.com>
|
||||
Li Zhang <zhlcindy@gmail.com>
|
||||
Li Zhang <zhlcindy@linux.vnet.ibm.com>
|
||||
Lluís Vilanova <vilanova@ac.upc.edu>
|
||||
Lluís Vilanova <xscript@gmx.net>
|
||||
Longpeng (Mike) <longpeng2@huawei.com>
|
||||
Luc Michel <luc.michel@git.antfield.fr>
|
||||
Luc Michel <luc.michel@greensocs.com>
|
||||
Marc Marí <marc.mari.barcelo@gmail.com>
|
||||
Marc Marí <markmb@redhat.com>
|
||||
Michael Avdienko <whitearchey@gmail.com>
|
||||
Michael S. Tsirkin <mst@redhat.com>
|
||||
Munkyu Im <munkyu.im@samsung.com>
|
||||
Nicholas Bellinger <nab@linux-iscsi.org>
|
||||
Nicholas Thomas <nick@bytemark.co.uk>
|
||||
Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
|
||||
Orit Wasserman <owasserm@redhat.com>
|
||||
Paolo Bonzini <pbonzini@redhat.com>
|
||||
Pavel Dovgaluk <dovgaluk@ispras.ru>
|
||||
Pavel Dovgaluk <pavel.dovgaluk@gmail.com>
|
||||
Pavel Dovgaluk <Pavel.Dovgaluk@ispras.ru>
|
||||
Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
Peter Crosthwaite <peter.crosthwaite@petalogix.com>
|
||||
Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||
Prasad J Pandit <pjp@fedoraproject.org>
|
||||
Prasad J Pandit <ppandit@redhat.com>
|
||||
Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
|
||||
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
||||
Remy Noel <remy.noel@blade-group.com>
|
||||
Roger Pau Monné <roger.pau@citrix.com>
|
||||
Shin'ichiro Kawasaki <kawasaki@juno.dti.ne.jp>
|
||||
Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
|
||||
Sochin Jiang <sochin.jiang@huawei.com>
|
||||
Takashi Yoshii <takasi-y@ops.dti.ne.jp>
|
||||
Thomas Huth <thuth@redhat.com>
|
||||
Thomas Knych <thomaswk@google.com>
|
||||
Timothy Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
|
||||
Tony Nguyen <tony.nguyen@bt.com>
|
||||
Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
||||
Vibi Sreenivasan <vibi_sreenivasan@cms.com>
|
||||
Vijaya Kumar K <vijayak@cavium.com>
|
||||
Vijaya Kumar K <Vijaya.Kumar@cavium.com>
|
||||
Vijay Kumar <vijaykumar@bravegnu.org>
|
||||
Vijay Kumar <vijaykumar@zilogic.com>
|
||||
Wang Guang <wang.guang55@zte.com.cn>
|
||||
Wenchao Xia <xiawenc@linux.vnet.ibm.com>
|
||||
Wenshuang Ma <kevinnma@tencent.com>
|
||||
Xiaoqiang Zhao <zxq_yx_007@163.com>
|
||||
Xinhua Cao <caoxinhua@huawei.com>
|
||||
Xiong Zhang <xiong.y.zhang@intel.com>
|
||||
Yin Yin <yin.yin@cs2c.com.cn>
|
||||
Yu-Chen Lin <npes87184@gmail.com>
|
||||
Yu-Chen Lin <npes87184@gmail.com> <yuchenlin@synology.com>
|
||||
YunQiang Su <syq@debian.org>
|
||||
YunQiang Su <ysu@wavecomp.com>
|
||||
Yuri Pudgorodskiy <yur@virtuozzo.com>
|
||||
Zhengui Li <lizhengui@huawei.com>
|
||||
Zhenwei Pi <pizhenwei@bytedance.com>
|
||||
Zhenwei Pi <zhenwei.pi@youruncloud.com>
|
||||
Zhuang Yanying <ann.zhuangyanying@huawei.com>
|
||||
@@ -1,20 +0,0 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# We want all the document formats
|
||||
formats: all
|
||||
|
||||
# For consistency, we require that QEMU's Sphinx extensions
|
||||
# run with at least the same minimum version of Python that
|
||||
# we require for other Python in our codebase (our conf.py
|
||||
# enforces this, and some code needs it.)
|
||||
python:
|
||||
version: 3.5
|
||||
+6
-6
@@ -26,14 +26,14 @@ env:
|
||||
- IMAGE=debian-ppc64el-cross
|
||||
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
|
||||
build:
|
||||
pre_ci:
|
||||
- make docker-image-${IMAGE} V=1
|
||||
pre_ci_boot:
|
||||
image_name: registry.gitlab.com/qemu-project/qemu/qemu/${IMAGE}
|
||||
image_tag: latest
|
||||
pull: true
|
||||
image_name: qemu
|
||||
image_tag: ${IMAGE}
|
||||
pull: false
|
||||
options: "-e HOME=/root"
|
||||
ci:
|
||||
- unset CC
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-docs ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
+97
-378
@@ -1,22 +1,11 @@
|
||||
# The current Travis default is a VM based 16.04 Xenial on GCE
|
||||
# Additional builds with specific requirements for a full VM need to
|
||||
# be added as additional matrix: entries later on
|
||||
os: linux
|
||||
dist: xenial
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
cache:
|
||||
# There is one cache per branch and compiler version.
|
||||
# characteristics of each job are used to identify the cache:
|
||||
# - OS name (currently only linux)
|
||||
# - OS distribution (for Linux, xenial, trusty, or precise)
|
||||
# - Names and values of visible environment variables set in .travis.yml or Settings panel
|
||||
timeout: 1200
|
||||
ccache: true
|
||||
pip: true
|
||||
directories:
|
||||
- $HOME/avocado/data/cache
|
||||
cache: ccache
|
||||
|
||||
|
||||
addons:
|
||||
@@ -28,7 +17,7 @@ addons:
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcc-4.8-dev
|
||||
- libgnutls28-dev
|
||||
- libgnutls-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
@@ -36,24 +25,25 @@ addons:
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- libpng12-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libsdl2-image-dev
|
||||
- libsdl1.2-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
|
||||
- sparse
|
||||
- uuid-dev
|
||||
- gcovr
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
homebrew:
|
||||
packages:
|
||||
- glib
|
||||
- pixman
|
||||
- gnu-sed
|
||||
update: true
|
||||
|
||||
|
||||
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
|
||||
@@ -69,229 +59,190 @@ notifications:
|
||||
|
||||
env:
|
||||
global:
|
||||
- SRC_DIR=".."
|
||||
- BUILD_DIR="build"
|
||||
- SRC_DIR="."
|
||||
- BUILD_DIR="."
|
||||
- BASE_CONFIG="--disable-docs --disable-tools"
|
||||
- TEST_BUILD_CMD=""
|
||||
- TEST_CMD="make check V=1"
|
||||
- TEST_CMD="make check -j3 V=1"
|
||||
# This is broadly a list of "mainline" softmmu targets which have support across the major distros
|
||||
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime"
|
||||
- CCACHE_MAXSIZE=1G
|
||||
- G_MESSAGES_DEBUG=error
|
||||
|
||||
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
|
||||
git:
|
||||
# we want to do this ourselves
|
||||
submodules: false
|
||||
|
||||
# Common first phase for all steps
|
||||
before_install:
|
||||
- if command -v ccache ; then ccache --zero-stats ; fi
|
||||
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
- echo "=== Using ${JOBS} simultaneous jobs ==="
|
||||
|
||||
# Configure step - may be overridden
|
||||
before_script:
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
|
||||
# Main build & test - rarely overridden - controlled by TEST_CMD
|
||||
script:
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] && [ -n "$TEST_BUILD_CMD" ]; then
|
||||
${TEST_BUILD_CMD} || BUILD_RC=$?
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] ; then
|
||||
${TEST_CMD} ;
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
after_script:
|
||||
- df -h
|
||||
- if command -v ccache ; then ccache --show-stats ; fi
|
||||
- make -j3 && travis_retry ${TEST_CMD}
|
||||
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
include:
|
||||
- name: "GCC static (user)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--disable-system --static"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
# we split the system builds as it takes a while to build them all
|
||||
- name: "GCC (main-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
- name: "GCC (other-softmmu)"
|
||||
env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
|
||||
# Just build tools and run minimal unit and softfloat checks
|
||||
- name: "GCC check-softfloat (user)"
|
||||
env:
|
||||
- env:
|
||||
- BASE_CONFIG="--enable-tools"
|
||||
- CONFIG="--disable-user --disable-system"
|
||||
- TEST_CMD="make check-unit check-softfloat -j${JOBS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
- TEST_CMD="make check-unit check-softfloat -j3"
|
||||
|
||||
- env:
|
||||
- CONFIG="--enable-debug --enable-debug-tcg --disable-user"
|
||||
|
||||
|
||||
# --enable-debug implies --enable-debug-tcg, also runs quite a bit slower
|
||||
- name: "GCC debug (main-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-debug --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug"
|
||||
|
||||
|
||||
# TCG debug can be run just on its own and is mostly agnostic to user/softmmu distinctions
|
||||
- name: "GCC debug (user)"
|
||||
env:
|
||||
# TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
|
||||
- env:
|
||||
- CONFIG="--enable-debug-tcg --disable-system"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
- name: "GCC some libs disabled (main-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
|
||||
# Module builds are mostly of interest to major distros
|
||||
- name: "GCC modules (main-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
# Alternate coroutines implementations are only really of interest to KVM users
|
||||
# However we can't test against KVM on Travis so we can only run unit tests
|
||||
- name: "check-unit coroutine=ucontext"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--with-coroutine=ucontext --disable-tcg"
|
||||
- TEST_CMD="make check-unit -j${JOBS} V=1"
|
||||
- TEST_CMD="make check-unit -j3 V=1"
|
||||
|
||||
|
||||
- name: "check-unit coroutine=sigaltstack"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--with-coroutine=sigaltstack --disable-tcg"
|
||||
- TEST_CMD="make check-unit -j${JOBS} V=1"
|
||||
- TEST_CMD="make check-unit -j3 V=1"
|
||||
|
||||
|
||||
# Check we can build docs and tools (out of tree)
|
||||
- name: "tools and docs (bionic)"
|
||||
dist: bionic
|
||||
env:
|
||||
- env:
|
||||
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
|
||||
- BASE_CONFIG="--enable-tools --enable-docs"
|
||||
- CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python3-sphinx
|
||||
- python-sphinx
|
||||
- texinfo
|
||||
- perl
|
||||
|
||||
|
||||
# Test with Clang for compile portability (Travis uses clang-5.0)
|
||||
- name: "Clang (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system --host-cc=clang --cxx=clang++"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
|
||||
- env:
|
||||
- CONFIG="--disable-system"
|
||||
compiler: clang
|
||||
|
||||
|
||||
- name: "Clang (main-softmmu)"
|
||||
env:
|
||||
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}
|
||||
--host-cc=clang --cxx=clang++"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize"
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} "
|
||||
compiler: clang
|
||||
before_script:
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
|
||||
- ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
- name: "Clang (other-softmmu)"
|
||||
env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}
|
||||
--host-cc=clang --cxx=clang++"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
compiler: clang
|
||||
|
||||
|
||||
# gprof/gcov are GCC features
|
||||
- name: "GCC gprof/gcov"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
after_success:
|
||||
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
|
||||
|
||||
|
||||
# We manually include builds which we disable "make check" for
|
||||
- name: "GCC without-default-devices (softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--without-default-devices --disable-user"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# Check the TCG interpreter (TCI)
|
||||
- name: "GCC TCI"
|
||||
env:
|
||||
- CONFIG="--enable-debug-tcg --enable-tcg-interpreter --disable-kvm --disable-containers
|
||||
--target-list=alpha-softmmu,arm-softmmu,hppa-softmmu,m68k-softmmu,microblaze-softmmu,moxie-softmmu,ppc-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- TEST_CMD="make check-qtest check-tcg V=1"
|
||||
# We manually include builds which we disable "make check" for
|
||||
- env:
|
||||
- CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# We don't need to exercise every backend with every front-end
|
||||
- name: "GCC trace log,simple,syslog (user)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
- name: "GCC trace ftrace (x86_64-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
- name: "GCC trace ust (x86_64-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# MacOSX builds
|
||||
- env:
|
||||
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
os: osx
|
||||
osx_image: xcode9.4
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
|
||||
os: osx
|
||||
osx_image: xcode10.2
|
||||
compiler: clang
|
||||
|
||||
|
||||
# Python builds
|
||||
- name: "GCC Python 3.5 (x86_64-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--target-list=x86_64-softmmu"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
language: python
|
||||
python: 3.5
|
||||
python:
|
||||
- "3.4"
|
||||
|
||||
|
||||
- name: "GCC Python 3.6 (x86_64-softmmu)"
|
||||
env:
|
||||
- env:
|
||||
- CONFIG="--target-list=x86_64-softmmu"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
language: python
|
||||
python: 3.6
|
||||
python:
|
||||
- "3.6"
|
||||
|
||||
|
||||
# Using newer GCC with sanitizers
|
||||
- name: "GCC9 with sanitizers (softmmu)"
|
||||
# Acceptance (Functional) tests
|
||||
- env:
|
||||
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make check-acceptance"
|
||||
after_failure:
|
||||
- cat tests/results/latest/job.log
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python3-pip
|
||||
- python3.5-venv
|
||||
# Using newer GCC with sanitizers
|
||||
- addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
@@ -316,8 +267,7 @@ jobs:
|
||||
- libpixman-1-dev
|
||||
- libpng12-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libsdl2-image-dev
|
||||
- libsdl1.2-dev
|
||||
- libseccomp-dev
|
||||
- libspice-protocol-dev
|
||||
- libspice-server-dev
|
||||
@@ -334,246 +284,15 @@ jobs:
|
||||
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user"
|
||||
- TEST_CMD=""
|
||||
before_script:
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread" --extra-ldflags="-fuse-ld=gold" || { cat config.log && exit 1; }
|
||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
# Run check-tcg against linux-user
|
||||
- name: "GCC check-tcg (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system --enable-debug-tcg"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
# Run check-tcg against linux-user (with plugins)
|
||||
# we skip sparc64-linux-user until it has been fixed somewhat
|
||||
# we skip cris-linux-user as it doesn't use the common run loop
|
||||
# we skip ppc64abi32-linux-user as it seems to have a broken libc
|
||||
- name: "GCC plugins check-tcg (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user,ppc64abi32-linux-user"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-system"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
|
||||
# Run check-tcg against softmmu targets
|
||||
- name: "GCC check-tcg (some-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
# Run check-tcg against softmmu targets (with plugins)
|
||||
- name: "GCC plugins check-tcg (some-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-plugins --enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
- name: "[aarch64] GCC check-tcg"
|
||||
arch: arm64
|
||||
dist: xenial
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- UNRELIABLE=true
|
||||
|
||||
- name: "[ppc64] GCC check-tcg"
|
||||
arch: ppc64le
|
||||
dist: xenial
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --target-list=ppc64-softmmu,ppc64le-linux-user"
|
||||
|
||||
- name: "[s390x] GCC check-tcg"
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
|
||||
- UNRELIABLE=true
|
||||
script:
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] ; then
|
||||
mv pc-bios/s390-ccw/*.img pc-bios/ ;
|
||||
${TEST_CMD} ;
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
|
||||
- name: "[s390x] GCC (other-softmmu)"
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libcap-ng-dev
|
||||
- libgnutls28-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- liblzo2-dev
|
||||
- libncurses-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libsdl2-dev
|
||||
- libsdl2-image-dev
|
||||
- libseccomp-dev
|
||||
- libsnappy-dev
|
||||
- libzstd-dev
|
||||
- nettle-dev
|
||||
- xfslibs-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- CONFIG="--disable-containers --audio-drv-list=sdl --disable-user
|
||||
--target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
- name: "[s390x] GCC (user)"
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
addons:
|
||||
apt_packages:
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
env:
|
||||
- CONFIG="--disable-containers --disable-system"
|
||||
|
||||
- name: "[s390x] Clang (disable-tcg)"
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
compiler: clang
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
env:
|
||||
- TEST_CMD="make check-unit"
|
||||
- CONFIG="--disable-containers --disable-tcg --enable-kvm
|
||||
--disable-tools --host-cc=clang --cxx=clang++"
|
||||
- UNRELIABLE=true
|
||||
|
||||
# Release builds
|
||||
# The make-release script expect a QEMU version, so our tag must start with a 'v'.
|
||||
# This is the case when release candidate tags are created.
|
||||
- name: "Release tarball"
|
||||
if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
env:
|
||||
# We want to build from the release tarball
|
||||
- BUILD_DIR="release/build/dir" SRC_DIR="../../.."
|
||||
- BASE_CONFIG="--prefix=$PWD/dist"
|
||||
- CONFIG="--target-list=x86_64-softmmu,aarch64-softmmu,armeb-linux-user,ppc-linux-user"
|
||||
- TEST_CMD="make install -j${JOBS}"
|
||||
- QEMU_VERSION="${TRAVIS_TAG:1}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
script:
|
||||
- make -C ${SRC_DIR} qemu-${QEMU_VERSION}.tar.bz2
|
||||
- ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2
|
||||
- tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION}
|
||||
- mkdir -p release-build && cd release-build
|
||||
- ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
- make install
|
||||
allow_failures:
|
||||
- env: UNRELIABLE=true
|
||||
- env:
|
||||
- CONFIG="--target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
QEMU Coding Style
|
||||
=================
|
||||
|
||||
Please use the script checkpatch.pl in the scripts directory to check
|
||||
patches before submitting.
|
||||
|
||||
1. Whitespace
|
||||
|
||||
Of course, the most important aspect in any coding style is whitespace.
|
||||
Crusty old coders who have trouble spotting the glasses on their noses
|
||||
can tell the difference between a tab and eight spaces from a distance
|
||||
of approximately fifteen parsecs. Many a flamewar has been fought and
|
||||
lost on this issue.
|
||||
|
||||
QEMU indents are four spaces. Tabs are never used, except in Makefiles
|
||||
where they have been irreversibly coded into the syntax.
|
||||
Spaces of course are superior to tabs because:
|
||||
|
||||
- You have just one way to specify whitespace, not two. Ambiguity breeds
|
||||
mistakes.
|
||||
- The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
|
||||
- Tab indents push your code to the right, making your screen seriously
|
||||
unbalanced.
|
||||
- Tabs will be rendered incorrectly on editors who are misconfigured not
|
||||
to use tab stops of eight positions.
|
||||
- Tabs are rendered badly in patches, causing off-by-one errors in almost
|
||||
every line.
|
||||
- It is the QEMU coding style.
|
||||
|
||||
Do not leave whitespace dangling off the ends of lines.
|
||||
|
||||
1.1 Multiline Indent
|
||||
|
||||
There are several places where indent is necessary:
|
||||
|
||||
- if/else
|
||||
- while/for
|
||||
- function definition & call
|
||||
|
||||
When breaking up a long line to fit within line width, we need a proper indent
|
||||
for the following lines.
|
||||
|
||||
In case of if/else, while/for, align the secondary lines just after the
|
||||
opening parenthesis of the first.
|
||||
|
||||
For example:
|
||||
|
||||
if (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
while (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
In case of function, there are several variants:
|
||||
|
||||
* 4 spaces indent from the beginning
|
||||
* align the secondary lines just after the opening parenthesis of the
|
||||
first
|
||||
|
||||
For example:
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, do_another(y,
|
||||
z));
|
||||
|
||||
2. Line width
|
||||
|
||||
Lines should be 80 characters; try not to make them longer.
|
||||
|
||||
Sometimes it is hard to do, especially when dealing with QEMU subsystems
|
||||
that use long function or symbol names. Even in that case, do not make
|
||||
lines much longer than 80 characters.
|
||||
|
||||
Rationale:
|
||||
- Some people like to tile their 24" screens with a 6x4 matrix of 80x24
|
||||
xterms and use vi in all of them. The best way to punish them is to
|
||||
let them keep doing it.
|
||||
- Code and especially patches is much more readable if limited to a sane
|
||||
line length. Eighty is traditional.
|
||||
- The four-space indentation makes the most common excuse ("But look
|
||||
at all that white space on the left!") moot.
|
||||
- It is the QEMU coding style.
|
||||
|
||||
3. Naming
|
||||
|
||||
Variables are lower_case_with_underscores; easy to type and read. Structured
|
||||
type names are in CamelCase; harder to type but standing out. Enum type
|
||||
names and function type names should also be in CamelCase. Scalar type
|
||||
names are lower_case_with_underscores_ending_with_a_t, like the POSIX
|
||||
uint64_t and family. Note that this last convention contradicts POSIX
|
||||
and is therefore likely to be changed.
|
||||
|
||||
When wrapping standard library functions, use the prefix qemu_ to alert
|
||||
readers that they are seeing a wrapped version; otherwise avoid this prefix.
|
||||
|
||||
4. Block structure
|
||||
|
||||
Every indented statement is braced; even if the block contains just one
|
||||
statement. The opening brace is on the line that contains the control
|
||||
flow statement that introduces the new block; the closing brace is on the
|
||||
same line as the else keyword, or on a line by itself if there is no else
|
||||
keyword. Example:
|
||||
|
||||
if (a == 5) {
|
||||
printf("a was 5.\n");
|
||||
} else if (a == 6) {
|
||||
printf("a was 6.\n");
|
||||
} else {
|
||||
printf("a was something else entirely.\n");
|
||||
}
|
||||
|
||||
Note that 'else if' is considered a single statement; otherwise a long if/
|
||||
else if/else if/.../else sequence would need an indent for every else
|
||||
statement.
|
||||
|
||||
An exception is the opening brace for a function; for reasons of tradition
|
||||
and clarity it comes on a line by itself:
|
||||
|
||||
void a_function(void)
|
||||
{
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: a consistent (except for functions...) bracing style reduces
|
||||
ambiguity and avoids needless churn when lines are added or removed.
|
||||
Furthermore, it is the QEMU coding style.
|
||||
|
||||
5. Declarations
|
||||
|
||||
Mixed declarations (interleaving statements and declarations within
|
||||
blocks) are generally not allowed; declarations should be at the beginning
|
||||
of blocks.
|
||||
|
||||
Every now and then, an exception is made for declarations inside a
|
||||
#ifdef or #ifndef block: if the code looks nicer, such declarations can
|
||||
be placed at the top of the block even if there are statements above.
|
||||
On the other hand, however, it's often best to move that #ifdef/#ifndef
|
||||
block to a separate function altogether.
|
||||
|
||||
6. Conditional statements
|
||||
|
||||
When comparing a variable for (in)equality with a constant, list the
|
||||
constant on the right, as in:
|
||||
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
|
||||
Besides, good compilers already warn users when '==' is mis-typed as '=',
|
||||
even when the constant is on the right.
|
||||
|
||||
7. Comment style
|
||||
|
||||
We use traditional C-style /* */ comments and avoid // comments.
|
||||
|
||||
Rationale: The // form is valid in C99, so this is purely a matter of
|
||||
consistency of style. The checkpatch script will warn you about this.
|
||||
|
||||
Multiline comment blocks should have a row of stars on the left,
|
||||
and the initial /* and terminating */ both on their own lines:
|
||||
/*
|
||||
* like
|
||||
* this
|
||||
*/
|
||||
This is the same format required by the Linux kernel coding style.
|
||||
|
||||
(Some of the existing comments in the codebase use the GNU Coding
|
||||
Standards form which does not have stars on the left, or other
|
||||
variations; avoid these when writing new comments, but don't worry
|
||||
about converting to the preferred form unless you're editing that
|
||||
comment anyway.)
|
||||
|
||||
Rationale: Consistency, and ease of visually picking out a multiline
|
||||
comment from the surrounding code.
|
||||
|
||||
8. trace-events style
|
||||
|
||||
8.1 0x prefix
|
||||
|
||||
In trace-events files, use a '0x' prefix to specify hex numbers, as in:
|
||||
|
||||
some_trace(unsigned x, uint64_t y) "x 0x%x y 0x" PRIx64
|
||||
|
||||
An exception is made for groups of numbers that are hexadecimal by
|
||||
convention and separated by the symbols '.', '/', ':', or ' ' (such as
|
||||
PCI bus id):
|
||||
|
||||
another_trace(int cssid, int ssid, int dev_num) "bus id: %x.%x.%04x"
|
||||
|
||||
However, you can use '0x' for such groups if you want. Anyway, be sure that
|
||||
it is obvious that numbers are in hex, ex.:
|
||||
|
||||
data_dump(uint8_t c1, uint8_t c2, uint8_t c3) "bytes (in hex): %02x %02x %02x"
|
||||
|
||||
Rationale: hex numbers are hard to read in logs when there is no 0x prefix,
|
||||
especially when (occasionally) the representation doesn't contain any letters
|
||||
and especially in one line with other decimal numbers. Number groups are allowed
|
||||
to not use '0x' because for some things notations like %x.%x.%x are used not
|
||||
only in Qemu. Also dumping raw data bytes with '0x' is less readable.
|
||||
|
||||
8.2 '#' printf flag
|
||||
|
||||
Do not use printf flag '#', like '%#x'.
|
||||
|
||||
Rationale: there are two ways to add a '0x' prefix to printed number: '0x%...'
|
||||
and '%#...'. For consistency the only one way should be used. Arguments for
|
||||
'0x%' are:
|
||||
- it is more popular
|
||||
- '%#' omits the 0x for the value 0 which makes output inconsistent
|
||||
@@ -1,641 +0,0 @@
|
||||
=================
|
||||
QEMU Coding Style
|
||||
=================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
Please use the script checkpatch.pl in the scripts directory to check
|
||||
patches before submitting.
|
||||
|
||||
Formatting and style
|
||||
********************
|
||||
|
||||
Whitespace
|
||||
==========
|
||||
|
||||
Of course, the most important aspect in any coding style is whitespace.
|
||||
Crusty old coders who have trouble spotting the glasses on their noses
|
||||
can tell the difference between a tab and eight spaces from a distance
|
||||
of approximately fifteen parsecs. Many a flamewar has been fought and
|
||||
lost on this issue.
|
||||
|
||||
QEMU indents are four spaces. Tabs are never used, except in Makefiles
|
||||
where they have been irreversibly coded into the syntax.
|
||||
Spaces of course are superior to tabs because:
|
||||
|
||||
* You have just one way to specify whitespace, not two. Ambiguity breeds
|
||||
mistakes.
|
||||
* The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
|
||||
* Tab indents push your code to the right, making your screen seriously
|
||||
unbalanced.
|
||||
* Tabs will be rendered incorrectly on editors who are misconfigured not
|
||||
to use tab stops of eight positions.
|
||||
* Tabs are rendered badly in patches, causing off-by-one errors in almost
|
||||
every line.
|
||||
* It is the QEMU coding style.
|
||||
|
||||
Do not leave whitespace dangling off the ends of lines.
|
||||
|
||||
Multiline Indent
|
||||
----------------
|
||||
|
||||
There are several places where indent is necessary:
|
||||
|
||||
* if/else
|
||||
* while/for
|
||||
* function definition & call
|
||||
|
||||
When breaking up a long line to fit within line width, we need a proper indent
|
||||
for the following lines.
|
||||
|
||||
In case of if/else, while/for, align the secondary lines just after the
|
||||
opening parenthesis of the first.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
if (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
while (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
In case of function, there are several variants:
|
||||
|
||||
* 4 spaces indent from the beginning
|
||||
* align the secondary lines just after the opening parenthesis of the first
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, do_another(y,
|
||||
z));
|
||||
|
||||
Line width
|
||||
==========
|
||||
|
||||
Lines should be 80 characters; try not to make them longer.
|
||||
|
||||
Sometimes it is hard to do, especially when dealing with QEMU subsystems
|
||||
that use long function or symbol names. Even in that case, do not make
|
||||
lines much longer than 80 characters.
|
||||
|
||||
Rationale:
|
||||
|
||||
* Some people like to tile their 24" screens with a 6x4 matrix of 80x24
|
||||
xterms and use vi in all of them. The best way to punish them is to
|
||||
let them keep doing it.
|
||||
* Code and especially patches is much more readable if limited to a sane
|
||||
line length. Eighty is traditional.
|
||||
* The four-space indentation makes the most common excuse ("But look
|
||||
at all that white space on the left!") moot.
|
||||
* It is the QEMU coding style.
|
||||
|
||||
Naming
|
||||
======
|
||||
|
||||
Variables are lower_case_with_underscores; easy to type and read. Structured
|
||||
type names are in CamelCase; harder to type but standing out. Enum type
|
||||
names and function type names should also be in CamelCase. Scalar type
|
||||
names are lower_case_with_underscores_ending_with_a_t, like the POSIX
|
||||
uint64_t and family. Note that this last convention contradicts POSIX
|
||||
and is therefore likely to be changed.
|
||||
|
||||
When wrapping standard library functions, use the prefix ``qemu_`` to alert
|
||||
readers that they are seeing a wrapped version; otherwise avoid this prefix.
|
||||
|
||||
Block structure
|
||||
===============
|
||||
|
||||
Every indented statement is braced; even if the block contains just one
|
||||
statement. The opening brace is on the line that contains the control
|
||||
flow statement that introduces the new block; the closing brace is on the
|
||||
same line as the else keyword, or on a line by itself if there is no else
|
||||
keyword. Example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
if (a == 5) {
|
||||
printf("a was 5.\n");
|
||||
} else if (a == 6) {
|
||||
printf("a was 6.\n");
|
||||
} else {
|
||||
printf("a was something else entirely.\n");
|
||||
}
|
||||
|
||||
Note that 'else if' is considered a single statement; otherwise a long if/
|
||||
else if/else if/.../else sequence would need an indent for every else
|
||||
statement.
|
||||
|
||||
An exception is the opening brace for a function; for reasons of tradition
|
||||
and clarity it comes on a line by itself:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void a_function(void)
|
||||
{
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: a consistent (except for functions...) bracing style reduces
|
||||
ambiguity and avoids needless churn when lines are added or removed.
|
||||
Furthermore, it is the QEMU coding style.
|
||||
|
||||
Declarations
|
||||
============
|
||||
|
||||
Mixed declarations (interleaving statements and declarations within
|
||||
blocks) are generally not allowed; declarations should be at the beginning
|
||||
of blocks.
|
||||
|
||||
Every now and then, an exception is made for declarations inside a
|
||||
#ifdef or #ifndef block: if the code looks nicer, such declarations can
|
||||
be placed at the top of the block even if there are statements above.
|
||||
On the other hand, however, it's often best to move that #ifdef/#ifndef
|
||||
block to a separate function altogether.
|
||||
|
||||
Conditional statements
|
||||
======================
|
||||
|
||||
When comparing a variable for (in)equality with a constant, list the
|
||||
constant on the right, as in:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
|
||||
Besides, good compilers already warn users when '==' is mis-typed as '=',
|
||||
even when the constant is on the right.
|
||||
|
||||
Comment style
|
||||
=============
|
||||
|
||||
We use traditional C-style /``*`` ``*``/ comments and avoid // comments.
|
||||
|
||||
Rationale: The // form is valid in C99, so this is purely a matter of
|
||||
consistency of style. The checkpatch script will warn you about this.
|
||||
|
||||
Multiline comment blocks should have a row of stars on the left,
|
||||
and the initial /``*`` and terminating ``*``/ both on their own lines:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/*
|
||||
* like
|
||||
* this
|
||||
*/
|
||||
|
||||
This is the same format required by the Linux kernel coding style.
|
||||
|
||||
(Some of the existing comments in the codebase use the GNU Coding
|
||||
Standards form which does not have stars on the left, or other
|
||||
variations; avoid these when writing new comments, but don't worry
|
||||
about converting to the preferred form unless you're editing that
|
||||
comment anyway.)
|
||||
|
||||
Rationale: Consistency, and ease of visually picking out a multiline
|
||||
comment from the surrounding code.
|
||||
|
||||
Language usage
|
||||
**************
|
||||
|
||||
Preprocessor
|
||||
============
|
||||
|
||||
Variadic macros
|
||||
---------------
|
||||
|
||||
For variadic macros, stick with this C99-like syntax:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
|
||||
|
||||
Include directives
|
||||
------------------
|
||||
|
||||
Order include directives as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "qemu/osdep.h" /* Always first... */
|
||||
#include <...> /* then system headers... */
|
||||
#include "..." /* and finally QEMU headers. */
|
||||
|
||||
The "qemu/osdep.h" header contains preprocessor macros that affect the behavior
|
||||
of core system headers like <stdint.h>. It must be the first include so that
|
||||
core system headers included by external libraries get the preprocessor macros
|
||||
that QEMU depends on.
|
||||
|
||||
Do not include "qemu/osdep.h" from header files since the .c file will have
|
||||
already included it.
|
||||
|
||||
C types
|
||||
=======
|
||||
|
||||
It should be common sense to use the right type, but we have collected
|
||||
a few useful guidelines here.
|
||||
|
||||
Scalars
|
||||
-------
|
||||
|
||||
If you're using "int" or "long", odds are good that there's a better type.
|
||||
If a variable is counting something, it should be declared with an
|
||||
unsigned type.
|
||||
|
||||
If it's host memory-size related, size_t should be a good choice (use
|
||||
ssize_t only if required). Guest RAM memory offsets must use ram_addr_t,
|
||||
but only for RAM, it may not cover whole guest address space.
|
||||
|
||||
If it's file-size related, use off_t.
|
||||
If it's file-offset related (i.e., signed), use off_t.
|
||||
If it's just counting small numbers use "unsigned int";
|
||||
(on all but oddball embedded systems, you can assume that that
|
||||
type is at least four bytes wide).
|
||||
|
||||
In the event that you require a specific width, use a standard type
|
||||
like int32_t, uint32_t, uint64_t, etc. The specific types are
|
||||
mandatory for VMState fields.
|
||||
|
||||
Don't use Linux kernel internal types like u32, __u32 or __le32.
|
||||
|
||||
Use hwaddr for guest physical addresses except pcibus_t
|
||||
for PCI addresses. In addition, ram_addr_t is a QEMU internal address
|
||||
space that maps guest RAM physical addresses into an intermediate
|
||||
address space that can map to host virtual address spaces. Generally
|
||||
speaking, the size of guest memory can always fit into ram_addr_t but
|
||||
it would not be correct to store an actual guest physical address in a
|
||||
ram_addr_t.
|
||||
|
||||
For CPU virtual addresses there are several possible types.
|
||||
vaddr is the best type to use to hold a CPU virtual address in
|
||||
target-independent code. It is guaranteed to be large enough to hold a
|
||||
virtual address for any target, and it does not change size from target
|
||||
to target. It is always unsigned.
|
||||
target_ulong is a type the size of a virtual address on the CPU; this means
|
||||
it may be 32 or 64 bits depending on which target is being built. It should
|
||||
therefore be used only in target-specific code, and in some
|
||||
performance-critical built-per-target core code such as the TLB code.
|
||||
There is also a signed version, target_long.
|
||||
abi_ulong is for the ``*``-user targets, and represents a type the size of
|
||||
'void ``*``' in that target's ABI. (This may not be the same as the size of a
|
||||
full CPU virtual address in the case of target ABIs which use 32 bit pointers
|
||||
on 64 bit CPUs, like sparc32plus.) Definitions of structures that must match
|
||||
the target's ABI must use this type for anything that on the target is defined
|
||||
to be an 'unsigned long' or a pointer type.
|
||||
There is also a signed version, abi_long.
|
||||
|
||||
Of course, take all of the above with a grain of salt. If you're about
|
||||
to use some system interface that requires a type like size_t, pid_t or
|
||||
off_t, use matching types for any corresponding variables.
|
||||
|
||||
Also, if you try to use e.g., "unsigned int" as a type, and that
|
||||
conflicts with the signedness of a related variable, sometimes
|
||||
it's best just to use the *wrong* type, if "pulling the thread"
|
||||
and fixing all related variables would be too invasive.
|
||||
|
||||
Finally, while using descriptive types is important, be careful not to
|
||||
go overboard. If whatever you're doing causes warnings, or requires
|
||||
casts, then reconsider or ask for help.
|
||||
|
||||
Pointers
|
||||
--------
|
||||
|
||||
Ensure that all of your pointers are "const-correct".
|
||||
Unless a pointer is used to modify the pointed-to storage,
|
||||
give it the "const" attribute. That way, the reader knows
|
||||
up-front that this is a read-only pointer. Perhaps more
|
||||
importantly, if we're diligent about this, when you see a non-const
|
||||
pointer, you're guaranteed that it is used to modify the storage
|
||||
it points to, or it is aliased to another pointer that is.
|
||||
|
||||
Typedefs
|
||||
--------
|
||||
|
||||
Typedefs are used to eliminate the redundant 'struct' keyword, since type
|
||||
names have a different style than other identifiers ("CamelCase" versus
|
||||
"snake_case"). Each named struct type should have a CamelCase name and a
|
||||
corresponding typedef.
|
||||
|
||||
Since certain C compilers choke on duplicated typedefs, you should avoid
|
||||
them and declare a typedef only in one header file. For common types,
|
||||
you can use "include/qemu/typedefs.h" for example. However, as a matter
|
||||
of convenience it is also perfectly fine to use forward struct
|
||||
definitions instead of typedefs in headers and function prototypes; this
|
||||
avoids problems with duplicated typedefs and reduces the need to include
|
||||
headers from other headers.
|
||||
|
||||
Reserved namespaces in C and POSIX
|
||||
----------------------------------
|
||||
|
||||
Underscore capital, double underscore, and underscore 't' suffixes should be
|
||||
avoided.
|
||||
|
||||
Low level memory management
|
||||
===========================
|
||||
|
||||
Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign
|
||||
APIs is not allowed in the QEMU codebase. Instead of these routines,
|
||||
use the GLib memory allocation routines g_malloc/g_malloc0/g_new/
|
||||
g_new0/g_realloc/g_free or QEMU's qemu_memalign/qemu_blockalign/qemu_vfree
|
||||
APIs.
|
||||
|
||||
Please note that g_malloc will exit on allocation failure, so there
|
||||
is no need to test for failure (as you would have to with malloc).
|
||||
Calling g_malloc with a zero size is valid and will return NULL.
|
||||
|
||||
Prefer g_new(T, n) instead of g_malloc(sizeof(T) ``*`` n) for the following
|
||||
reasons:
|
||||
|
||||
* It catches multiplication overflowing size_t;
|
||||
* It returns T ``*`` instead of void ``*``, letting compiler catch more type errors.
|
||||
|
||||
Declarations like
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
T *v = g_malloc(sizeof(*v))
|
||||
|
||||
are acceptable, though.
|
||||
|
||||
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
|
||||
qemu_vfree, since breaking this will cause problems on Win32.
|
||||
|
||||
String manipulation
|
||||
===================
|
||||
|
||||
Do not use the strncpy function. As mentioned in the man page, it does *not*
|
||||
guarantee a NULL-terminated buffer, which makes it extremely dangerous to use.
|
||||
It also zeros trailing destination bytes out to the specified length. Instead,
|
||||
use this similar function when possible, but note its different signature:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void pstrcpy(char *dest, int dest_buf_size, const char *src)
|
||||
|
||||
Don't use strcat because it can't check for buffer overflows, but:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
|
||||
The same limitation exists with sprintf and vsprintf, so use snprintf and
|
||||
vsnprintf.
|
||||
|
||||
QEMU provides other useful string functions:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int strstart(const char *str, const char *val, const char **ptr)
|
||||
int stristart(const char *str, const char *val, const char **ptr)
|
||||
int qemu_strnlen(const char *s, int max_len)
|
||||
|
||||
There are also replacement character processing macros for isxyz and toxyz,
|
||||
so instead of e.g. isalnum you should use qemu_isalnum.
|
||||
|
||||
Because of the memory management rules, you must use g_strdup/g_strndup
|
||||
instead of plain strdup/strndup.
|
||||
|
||||
Printf-style functions
|
||||
======================
|
||||
|
||||
Whenever you add a new printf-style function, i.e., one with a format
|
||||
string argument and following "..." in its prototype, be sure to use
|
||||
gcc's printf attribute directive in the prototype.
|
||||
|
||||
This makes it so gcc's -Wformat and -Wformat-security options can do
|
||||
their jobs and cross-check format strings with the number and types
|
||||
of arguments.
|
||||
|
||||
C standard, implementation defined and undefined behaviors
|
||||
==========================================================
|
||||
|
||||
C code in QEMU should be written to the C99 language specification. A copy
|
||||
of the final version of the C99 standard with corrigenda TC1, TC2, and TC3
|
||||
included, formatted as a draft, can be downloaded from:
|
||||
|
||||
`<http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf>`_
|
||||
|
||||
The C language specification defines regions of undefined behavior and
|
||||
implementation defined behavior (to give compiler authors enough leeway to
|
||||
produce better code). In general, code in QEMU should follow the language
|
||||
specification and avoid both undefined and implementation defined
|
||||
constructs. ("It works fine on the gcc I tested it with" is not a valid
|
||||
argument...) However there are a few areas where we allow ourselves to
|
||||
assume certain behaviors because in practice all the platforms we care about
|
||||
behave in the same way and writing strictly conformant code would be
|
||||
painful. These are:
|
||||
|
||||
* you may assume that integers are 2s complement representation
|
||||
* you may assume that right shift of a signed integer duplicates
|
||||
the sign bit (ie it is an arithmetic shift, not a logical shift)
|
||||
|
||||
In addition, QEMU assumes that the compiler does not use the latitude
|
||||
given in C99 and C11 to treat aspects of signed '<<' as undefined, as
|
||||
documented in the GNU Compiler Collection manual starting at version 4.0.
|
||||
|
||||
Automatic memory deallocation
|
||||
=============================
|
||||
|
||||
QEMU has a mandatory dependency either the GCC or CLang compiler. As
|
||||
such it has the freedom to make use of a C language extension for
|
||||
automatically running a cleanup function when a stack variable goes
|
||||
out of scope. This can be used to simplify function cleanup paths,
|
||||
often allowing many goto jumps to be eliminated, through automatic
|
||||
free'ing of memory.
|
||||
|
||||
The GLib2 library provides a number of functions/macros for enabling
|
||||
automatic cleanup:
|
||||
|
||||
`<https://developer.gnome.org/glib/stable/glib-Miscellaneous-Macros.html>`_
|
||||
|
||||
Most notably:
|
||||
|
||||
* g_autofree - will invoke g_free() on the variable going out of scope
|
||||
|
||||
* g_autoptr - for structs / objects, will invoke the cleanup func created
|
||||
by a previous use of G_DEFINE_AUTOPTR_CLEANUP_FUNC. This is
|
||||
supported for most GLib data types and GObjects
|
||||
|
||||
For example, instead of
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int somefunc(void) {
|
||||
int ret = -1;
|
||||
char *foo = g_strdup_printf("foo%", "wibble");
|
||||
GList *bar = .....
|
||||
|
||||
if (eek) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
g_free(foo);
|
||||
g_list_free(bar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Using g_autofree/g_autoptr enables the code to be written as:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int somefunc(void) {
|
||||
g_autofree char *foo = g_strdup_printf("foo%", "wibble");
|
||||
g_autoptr (GList) bar = .....
|
||||
|
||||
if (eek) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
While this generally results in simpler, less leak-prone code, there
|
||||
are still some caveats to beware of
|
||||
|
||||
* Variables declared with g_auto* MUST always be initialized,
|
||||
otherwise the cleanup function will use uninitialized stack memory
|
||||
|
||||
* If a variable declared with g_auto* holds a value which must
|
||||
live beyond the life of the function, that value must be saved
|
||||
and the original variable NULL'd out. This can be simpler using
|
||||
g_steal_pointer
|
||||
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
char *somefunc(void) {
|
||||
g_autofree char *foo = g_strdup_printf("foo%", "wibble");
|
||||
g_autoptr (GList) bar = .....
|
||||
|
||||
if (eek) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&foo);
|
||||
}
|
||||
|
||||
|
||||
QEMU Specific Idioms
|
||||
********************
|
||||
|
||||
Error handling and reporting
|
||||
============================
|
||||
|
||||
Reporting errors to the human user
|
||||
----------------------------------
|
||||
|
||||
Do not use printf(), fprintf() or monitor_printf(). Instead, use
|
||||
error_report() or error_vreport() from error-report.h. This ensures the
|
||||
error is reported in the right place (current monitor or stderr), and in
|
||||
a uniform format.
|
||||
|
||||
Use error_printf() & friends to print additional information.
|
||||
|
||||
error_report() prints the current location. In certain common cases
|
||||
like command line parsing, the current location is tracked
|
||||
automatically. To manipulate it manually, use the loc_``*``() from
|
||||
error-report.h.
|
||||
|
||||
Propagating errors
|
||||
------------------
|
||||
|
||||
An error can't always be reported to the user right where it's detected,
|
||||
but often needs to be propagated up the call chain to a place that can
|
||||
handle it. This can be done in various ways.
|
||||
|
||||
The most flexible one is Error objects. See error.h for usage
|
||||
information.
|
||||
|
||||
Use the simplest suitable method to communicate success / failure to
|
||||
callers. Stick to common methods: non-negative on success / -1 on
|
||||
error, non-negative / -errno, non-null / null, or Error objects.
|
||||
|
||||
Example: when a function returns a non-null pointer on success, and it
|
||||
can fail only in one way (as far as the caller is concerned), returning
|
||||
null on failure is just fine, and certainly simpler and a lot easier on
|
||||
the eyes than propagating an Error object through an Error ``*````*`` parameter.
|
||||
|
||||
Example: when a function's callers need to report details on failure
|
||||
only the function really knows, use Error ``*````*``, and set suitable errors.
|
||||
|
||||
Do not report an error to the user when you're also returning an error
|
||||
for somebody else to handle. Leave the reporting to the place that
|
||||
consumes the error returned.
|
||||
|
||||
Handling errors
|
||||
---------------
|
||||
|
||||
Calling exit() is fine when handling configuration errors during
|
||||
startup. It's problematic during normal operation. In particular,
|
||||
monitor commands should never exit().
|
||||
|
||||
Do not call exit() or abort() to handle an error that can be triggered
|
||||
by the guest (e.g., some unimplemented corner case in guest code
|
||||
translation or device emulation). Guests should not be able to
|
||||
terminate QEMU.
|
||||
|
||||
Note that &error_fatal is just another way to exit(1), and &error_abort
|
||||
is just another way to abort().
|
||||
|
||||
|
||||
trace-events style
|
||||
==================
|
||||
|
||||
0x prefix
|
||||
---------
|
||||
|
||||
In trace-events files, use a '0x' prefix to specify hex numbers, as in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
some_trace(unsigned x, uint64_t y) "x 0x%x y 0x" PRIx64
|
||||
|
||||
An exception is made for groups of numbers that are hexadecimal by
|
||||
convention and separated by the symbols '.', '/', ':', or ' ' (such as
|
||||
PCI bus id):
|
||||
|
||||
.. code-block::
|
||||
|
||||
another_trace(int cssid, int ssid, int dev_num) "bus id: %x.%x.%04x"
|
||||
|
||||
However, you can use '0x' for such groups if you want. Anyway, be sure that
|
||||
it is obvious that numbers are in hex, ex.:
|
||||
|
||||
.. code-block::
|
||||
|
||||
data_dump(uint8_t c1, uint8_t c2, uint8_t c3) "bytes (in hex): %02x %02x %02x"
|
||||
|
||||
Rationale: hex numbers are hard to read in logs when there is no 0x prefix,
|
||||
especially when (occasionally) the representation doesn't contain any letters
|
||||
and especially in one line with other decimal numbers. Number groups are allowed
|
||||
to not use '0x' because for some things notations like %x.%x.%x are used not
|
||||
only in Qemu. Also dumping raw data bytes with '0x' is less readable.
|
||||
|
||||
'#' printf flag
|
||||
---------------
|
||||
|
||||
Do not use printf flag '#', like '%#x'.
|
||||
|
||||
Rationale: there are two ways to add a '0x' prefix to printed number: '0x%...'
|
||||
and '%#...'. For consistency the only one way should be used. Arguments for
|
||||
'0x%' are:
|
||||
|
||||
* it is more popular
|
||||
* '%#' omits the 0x for the value 0 which makes output inconsistent
|
||||
@@ -0,0 +1,245 @@
|
||||
1. Preprocessor
|
||||
|
||||
1.1. Variadic macros
|
||||
|
||||
For variadic macros, stick with this C99-like syntax:
|
||||
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
|
||||
|
||||
1.2. Include directives
|
||||
|
||||
Order include directives as follows:
|
||||
|
||||
#include "qemu/osdep.h" /* Always first... */
|
||||
#include <...> /* then system headers... */
|
||||
#include "..." /* and finally QEMU headers. */
|
||||
|
||||
The "qemu/osdep.h" header contains preprocessor macros that affect the behavior
|
||||
of core system headers like <stdint.h>. It must be the first include so that
|
||||
core system headers included by external libraries get the preprocessor macros
|
||||
that QEMU depends on.
|
||||
|
||||
Do not include "qemu/osdep.h" from header files since the .c file will have
|
||||
already included it.
|
||||
|
||||
2. C types
|
||||
|
||||
It should be common sense to use the right type, but we have collected
|
||||
a few useful guidelines here.
|
||||
|
||||
2.1. Scalars
|
||||
|
||||
If you're using "int" or "long", odds are good that there's a better type.
|
||||
If a variable is counting something, it should be declared with an
|
||||
unsigned type.
|
||||
|
||||
If it's host memory-size related, size_t should be a good choice (use
|
||||
ssize_t only if required). Guest RAM memory offsets must use ram_addr_t,
|
||||
but only for RAM, it may not cover whole guest address space.
|
||||
|
||||
If it's file-size related, use off_t.
|
||||
If it's file-offset related (i.e., signed), use off_t.
|
||||
If it's just counting small numbers use "unsigned int";
|
||||
(on all but oddball embedded systems, you can assume that that
|
||||
type is at least four bytes wide).
|
||||
|
||||
In the event that you require a specific width, use a standard type
|
||||
like int32_t, uint32_t, uint64_t, etc. The specific types are
|
||||
mandatory for VMState fields.
|
||||
|
||||
Don't use Linux kernel internal types like u32, __u32 or __le32.
|
||||
|
||||
Use hwaddr for guest physical addresses except pcibus_t
|
||||
for PCI addresses. In addition, ram_addr_t is a QEMU internal address
|
||||
space that maps guest RAM physical addresses into an intermediate
|
||||
address space that can map to host virtual address spaces. Generally
|
||||
speaking, the size of guest memory can always fit into ram_addr_t but
|
||||
it would not be correct to store an actual guest physical address in a
|
||||
ram_addr_t.
|
||||
|
||||
For CPU virtual addresses there are several possible types.
|
||||
vaddr is the best type to use to hold a CPU virtual address in
|
||||
target-independent code. It is guaranteed to be large enough to hold a
|
||||
virtual address for any target, and it does not change size from target
|
||||
to target. It is always unsigned.
|
||||
target_ulong is a type the size of a virtual address on the CPU; this means
|
||||
it may be 32 or 64 bits depending on which target is being built. It should
|
||||
therefore be used only in target-specific code, and in some
|
||||
performance-critical built-per-target core code such as the TLB code.
|
||||
There is also a signed version, target_long.
|
||||
abi_ulong is for the *-user targets, and represents a type the size of
|
||||
'void *' in that target's ABI. (This may not be the same as the size of a
|
||||
full CPU virtual address in the case of target ABIs which use 32 bit pointers
|
||||
on 64 bit CPUs, like sparc32plus.) Definitions of structures that must match
|
||||
the target's ABI must use this type for anything that on the target is defined
|
||||
to be an 'unsigned long' or a pointer type.
|
||||
There is also a signed version, abi_long.
|
||||
|
||||
Of course, take all of the above with a grain of salt. If you're about
|
||||
to use some system interface that requires a type like size_t, pid_t or
|
||||
off_t, use matching types for any corresponding variables.
|
||||
|
||||
Also, if you try to use e.g., "unsigned int" as a type, and that
|
||||
conflicts with the signedness of a related variable, sometimes
|
||||
it's best just to use the *wrong* type, if "pulling the thread"
|
||||
and fixing all related variables would be too invasive.
|
||||
|
||||
Finally, while using descriptive types is important, be careful not to
|
||||
go overboard. If whatever you're doing causes warnings, or requires
|
||||
casts, then reconsider or ask for help.
|
||||
|
||||
2.2. Pointers
|
||||
|
||||
Ensure that all of your pointers are "const-correct".
|
||||
Unless a pointer is used to modify the pointed-to storage,
|
||||
give it the "const" attribute. That way, the reader knows
|
||||
up-front that this is a read-only pointer. Perhaps more
|
||||
importantly, if we're diligent about this, when you see a non-const
|
||||
pointer, you're guaranteed that it is used to modify the storage
|
||||
it points to, or it is aliased to another pointer that is.
|
||||
|
||||
2.3. Typedefs
|
||||
Typedefs are used to eliminate the redundant 'struct' keyword.
|
||||
|
||||
2.4. Reserved namespaces in C and POSIX
|
||||
Underscore capital, double underscore, and underscore 't' suffixes should be
|
||||
avoided.
|
||||
|
||||
3. Low level memory management
|
||||
|
||||
Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign
|
||||
APIs is not allowed in the QEMU codebase. Instead of these routines,
|
||||
use the GLib memory allocation routines g_malloc/g_malloc0/g_new/
|
||||
g_new0/g_realloc/g_free or QEMU's qemu_memalign/qemu_blockalign/qemu_vfree
|
||||
APIs.
|
||||
|
||||
Please note that g_malloc will exit on allocation failure, so there
|
||||
is no need to test for failure (as you would have to with malloc).
|
||||
Calling g_malloc with a zero size is valid and will return NULL.
|
||||
|
||||
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
|
||||
reasons:
|
||||
|
||||
a. It catches multiplication overflowing size_t;
|
||||
b. It returns T * instead of void *, letting compiler catch more type
|
||||
errors.
|
||||
|
||||
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
|
||||
|
||||
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
|
||||
qemu_vfree, since breaking this will cause problems on Win32.
|
||||
|
||||
4. String manipulation
|
||||
|
||||
Do not use the strncpy function. As mentioned in the man page, it does *not*
|
||||
guarantee a NULL-terminated buffer, which makes it extremely dangerous to use.
|
||||
It also zeros trailing destination bytes out to the specified length. Instead,
|
||||
use this similar function when possible, but note its different signature:
|
||||
void pstrcpy(char *dest, int dest_buf_size, const char *src)
|
||||
|
||||
Don't use strcat because it can't check for buffer overflows, but:
|
||||
char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
|
||||
The same limitation exists with sprintf and vsprintf, so use snprintf and
|
||||
vsnprintf.
|
||||
|
||||
QEMU provides other useful string functions:
|
||||
int strstart(const char *str, const char *val, const char **ptr)
|
||||
int stristart(const char *str, const char *val, const char **ptr)
|
||||
int qemu_strnlen(const char *s, int max_len)
|
||||
|
||||
There are also replacement character processing macros for isxyz and toxyz,
|
||||
so instead of e.g. isalnum you should use qemu_isalnum.
|
||||
|
||||
Because of the memory management rules, you must use g_strdup/g_strndup
|
||||
instead of plain strdup/strndup.
|
||||
|
||||
5. Printf-style functions
|
||||
|
||||
Whenever you add a new printf-style function, i.e., one with a format
|
||||
string argument and following "..." in its prototype, be sure to use
|
||||
gcc's printf attribute directive in the prototype.
|
||||
|
||||
This makes it so gcc's -Wformat and -Wformat-security options can do
|
||||
their jobs and cross-check format strings with the number and types
|
||||
of arguments.
|
||||
|
||||
6. C standard, implementation defined and undefined behaviors
|
||||
|
||||
C code in QEMU should be written to the C99 language specification. A copy
|
||||
of the final version of the C99 standard with corrigenda TC1, TC2, and TC3
|
||||
included, formatted as a draft, can be downloaded from:
|
||||
http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
|
||||
|
||||
The C language specification defines regions of undefined behavior and
|
||||
implementation defined behavior (to give compiler authors enough leeway to
|
||||
produce better code). In general, code in QEMU should follow the language
|
||||
specification and avoid both undefined and implementation defined
|
||||
constructs. ("It works fine on the gcc I tested it with" is not a valid
|
||||
argument...) However there are a few areas where we allow ourselves to
|
||||
assume certain behaviors because in practice all the platforms we care about
|
||||
behave in the same way and writing strictly conformant code would be
|
||||
painful. These are:
|
||||
* you may assume that integers are 2s complement representation
|
||||
* you may assume that right shift of a signed integer duplicates
|
||||
the sign bit (ie it is an arithmetic shift, not a logical shift)
|
||||
|
||||
In addition, QEMU assumes that the compiler does not use the latitude
|
||||
given in C99 and C11 to treat aspects of signed '<<' as undefined, as
|
||||
documented in the GNU Compiler Collection manual starting at version 4.0.
|
||||
|
||||
7. Error handling and reporting
|
||||
|
||||
7.1 Reporting errors to the human user
|
||||
|
||||
Do not use printf(), fprintf() or monitor_printf(). Instead, use
|
||||
error_report() or error_vreport() from error-report.h. This ensures the
|
||||
error is reported in the right place (current monitor or stderr), and in
|
||||
a uniform format.
|
||||
|
||||
Use error_printf() & friends to print additional information.
|
||||
|
||||
error_report() prints the current location. In certain common cases
|
||||
like command line parsing, the current location is tracked
|
||||
automatically. To manipulate it manually, use the loc_*() from
|
||||
error-report.h.
|
||||
|
||||
7.2 Propagating errors
|
||||
|
||||
An error can't always be reported to the user right where it's detected,
|
||||
but often needs to be propagated up the call chain to a place that can
|
||||
handle it. This can be done in various ways.
|
||||
|
||||
The most flexible one is Error objects. See error.h for usage
|
||||
information.
|
||||
|
||||
Use the simplest suitable method to communicate success / failure to
|
||||
callers. Stick to common methods: non-negative on success / -1 on
|
||||
error, non-negative / -errno, non-null / null, or Error objects.
|
||||
|
||||
Example: when a function returns a non-null pointer on success, and it
|
||||
can fail only in one way (as far as the caller is concerned), returning
|
||||
null on failure is just fine, and certainly simpler and a lot easier on
|
||||
the eyes than propagating an Error object through an Error ** parameter.
|
||||
|
||||
Example: when a function's callers need to report details on failure
|
||||
only the function really knows, use Error **, and set suitable errors.
|
||||
|
||||
Do not report an error to the user when you're also returning an error
|
||||
for somebody else to handle. Leave the reporting to the place that
|
||||
consumes the error returned.
|
||||
|
||||
7.3 Handling errors
|
||||
|
||||
Calling exit() is fine when handling configuration errors during
|
||||
startup. It's problematic during normal operation. In particular,
|
||||
monitor commands should never exit().
|
||||
|
||||
Do not call exit() or abort() to handle an error that can be triggered
|
||||
by the guest (e.g., some unimplemented corner case in guest code
|
||||
translation or device emulation). Guests should not be able to
|
||||
terminate QEMU.
|
||||
|
||||
Note that &error_fatal is just another way to exit(1), and &error_abort
|
||||
is just another way to abort().
|
||||
@@ -1,4 +0,0 @@
|
||||
source Kconfig.host
|
||||
source backends/Kconfig
|
||||
source accel/Kconfig
|
||||
source hw/Kconfig
|
||||
+4
-3
@@ -2,6 +2,9 @@
|
||||
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
|
||||
# these two need to be kept in sync.
|
||||
|
||||
config KVM
|
||||
bool
|
||||
|
||||
config LINUX
|
||||
bool
|
||||
|
||||
@@ -22,11 +25,9 @@ config TPM
|
||||
|
||||
config VHOST_USER
|
||||
bool
|
||||
select VHOST
|
||||
|
||||
config VHOST_KERNEL
|
||||
config XEN
|
||||
bool
|
||||
select VHOST
|
||||
|
||||
config VIRTFS
|
||||
bool
|
||||
|
||||
@@ -18,9 +18,8 @@ As of July 2013, contributions under version 2 of the GNU General Public
|
||||
License (and no later version) are only accepted for the following files
|
||||
or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*.
|
||||
|
||||
3) The Tiny Code Generator (TCG) is mostly under the BSD or MIT licenses;
|
||||
but some parts may be GPLv2 or other licenses. Again, see the
|
||||
specific licensing information in each source file.
|
||||
3) The Tiny Code Generator (TCG) is released under the BSD license
|
||||
(see license headers in files).
|
||||
|
||||
4) QEMU is a trademark of Fabrice Bellard.
|
||||
|
||||
|
||||
+142
-606
File diff suppressed because it is too large.
Load diff
@@ -15,6 +15,9 @@ UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
|
||||
help check-help print-% \
|
||||
docker docker-% vm-help vm-test vm-build-%
|
||||
|
||||
print-%:
|
||||
@echo '$*=$($*)'
|
||||
|
||||
# All following code might depend on configuration variables
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
# Put the all: rule here so that config-host.mak can contain dependencies.
|
||||
@@ -71,12 +74,6 @@ CONFIG_ALL=y
|
||||
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION
|
||||
@echo $@ is out-of-date, running configure
|
||||
@./config.status
|
||||
|
||||
# Force configure to re-run if the API symbols are updated
|
||||
ifeq ($(CONFIG_PLUGIN),y)
|
||||
config-host.mak: $(SRC_PATH)/plugins/qemu-plugins.symbols
|
||||
endif
|
||||
|
||||
else
|
||||
config-host.mak:
|
||||
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
|
||||
@@ -87,7 +84,8 @@ endif
|
||||
|
||||
include $(SRC_PATH)/rules.mak
|
||||
|
||||
# lor is defined in rules.mak
|
||||
# notempy and lor are defined in rules.mak
|
||||
CONFIG_TOOLS := $(call notempty,$(TOOLS))
|
||||
CONFIG_BLOCK := $(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS))
|
||||
|
||||
# Create QEMU_PKGVERSION and FULL_VERSION strings
|
||||
@@ -114,7 +112,6 @@ GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
|
||||
GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h)
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c)
|
||||
GENERATED_QAPI_FILES += qapi/qapi-init-commands.h qapi/qapi-init-commands.c
|
||||
GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h)
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c)
|
||||
@@ -125,28 +122,7 @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
|
||||
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
|
||||
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
|
||||
|
||||
# The following list considers only the storage daemon main module. All other
|
||||
# modules are currently shared with the main schema, so we don't actually
|
||||
# generate additional files.
|
||||
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi
|
||||
|
||||
generated-files-y += $(GENERATED_QAPI_FILES)
|
||||
generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES)
|
||||
|
||||
generated-files-y += trace/generated-tcg-tracers.h
|
||||
|
||||
@@ -336,49 +312,30 @@ $(call set-vpath, $(SRC_PATH))
|
||||
LIBS+=-lz $(LIBS_TOOLS)
|
||||
|
||||
vhost-user-json-y =
|
||||
HELPERS-y = $(HELPERS)
|
||||
HELPERS-y =
|
||||
|
||||
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) += qemu-bridge-helper$(EXESUF)
|
||||
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
|
||||
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_VIRGL)$(CONFIG_GBM)$(CONFIG_TOOLS),yyyy)
|
||||
ifdef CONFIG_LINUX
|
||||
ifdef CONFIG_VIRGL
|
||||
ifdef CONFIG_GBM
|
||||
HELPERS-y += vhost-user-gpu$(EXESUF)
|
||||
vhost-user-json-y += contrib/vhost-user-gpu/50-qemu-gpu.json
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SOFTMMU)$(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyyy)
|
||||
HELPERS-y += virtiofsd$(EXESUF)
|
||||
vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json
|
||||
endif
|
||||
|
||||
# Sphinx does not allow building manuals into the same directory as
|
||||
# the source files, so if we're doing an in-tree QEMU build we must
|
||||
# build the manuals into a subdirectory (and then install them from
|
||||
# there for 'make install'). For an out-of-tree build we can just
|
||||
# use the docs/ subdirectory in the build tree as normal.
|
||||
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
|
||||
MANUAL_BUILDDIR := docs/built
|
||||
else
|
||||
MANUAL_BUILDDIR := docs
|
||||
endif
|
||||
|
||||
ifdef BUILD_DOCS
|
||||
DOCS+=$(MANUAL_BUILDDIR)/system/qemu.1
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-img.1
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-nbd.8
|
||||
DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga.8
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1
|
||||
endif
|
||||
DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7
|
||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||
DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7
|
||||
DOCS+=$(MANUAL_BUILDDIR)/index.html
|
||||
DOCS+=docs/qemu-block-drivers.7
|
||||
DOCS+=docs/qemu-cpu-models.7
|
||||
ifdef CONFIG_VIRTFS
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1
|
||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||
endif
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1
|
||||
DOCS+=scripts/qemu-trace-stap.1
|
||||
endif
|
||||
else
|
||||
DOCS=
|
||||
@@ -404,8 +361,7 @@ endif
|
||||
# This has to be kept in sync with Kconfig.host.
|
||||
MINIKCONF_ARGS = \
|
||||
$(CONFIG_MINIKCONF_MODE) \
|
||||
$@ $*/config-devices.mak.d $< $(SRC_PATH)/Kconfig \
|
||||
CONFIG_TCG=$(CONFIG_TCG) \
|
||||
$@ $*/config-devices.mak.d $< $(MINIKCONF_INPUTS) \
|
||||
CONFIG_KVM=$(CONFIG_KVM) \
|
||||
CONFIG_SPICE=$(CONFIG_SPICE) \
|
||||
CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
|
||||
@@ -414,16 +370,15 @@ MINIKCONF_ARGS = \
|
||||
CONFIG_OPENGL=$(CONFIG_OPENGL) \
|
||||
CONFIG_X11=$(CONFIG_X11) \
|
||||
CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
|
||||
CONFIG_VHOST_KERNEL=$(CONFIG_VHOST_KERNEL) \
|
||||
CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
|
||||
CONFIG_LINUX=$(CONFIG_LINUX) \
|
||||
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
|
||||
|
||||
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py
|
||||
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
|
||||
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
|
||||
|
||||
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(SRC_PATH)/Kconfig $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) \
|
||||
> $@.tmp, "GEN", "$@.tmp")
|
||||
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
|
||||
$(call quiet-command, if test -f $@; then \
|
||||
if cmp -s $@.old $@; then \
|
||||
mv $@.tmp $@; \
|
||||
@@ -458,7 +413,6 @@ dummy := $(call unnest-vars,, \
|
||||
elf2dmp-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
virtiofsd-obj-y \
|
||||
rdmacm-mux-obj-y \
|
||||
libvhost-user-obj-y \
|
||||
vhost-user-scsi-obj-y \
|
||||
@@ -468,13 +422,16 @@ dummy := $(call unnest-vars,, \
|
||||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
storage-daemon-obj-y \
|
||||
storage-daemon-obj-m \
|
||||
crypto-obj-y \
|
||||
crypto-user-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m \
|
||||
ui-obj-y \
|
||||
ui-obj-m \
|
||||
audio-obj-y \
|
||||
audio-obj-m \
|
||||
trace-obj-y)
|
||||
|
||||
include $(SRC_PATH)/tests/Makefile.include
|
||||
@@ -497,28 +454,16 @@ config-host.h-timestamp: config-host.mak
|
||||
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
|
||||
|
||||
TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
|
||||
TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
|
||||
|
||||
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
|
||||
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(chardev-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): config-all-devices.mak
|
||||
ifdef DECOMPRESS_EDK2_BLOBS
|
||||
$(SOFTMMU_ALL_RULES): $(edk2-decompressed)
|
||||
endif
|
||||
|
||||
SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES))
|
||||
$(SOFTMMU_FUZZ_RULES): $(authz-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): config-all-devices.mak
|
||||
$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
|
||||
|
||||
.PHONY: $(TARGET_DIRS_RULES)
|
||||
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
|
||||
@@ -526,14 +471,13 @@ $(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
|
||||
$(TARGET_DIRS_RULES):
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
|
||||
|
||||
# LIBFDT_lib="": avoid breaking existing trees with objects requiring -fPIC
|
||||
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_lib=""
|
||||
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_srcdir=$(SRC_PATH)/dtc/libfdt
|
||||
DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS)
|
||||
DTC_CPPFLAGS=-I$(SRC_PATH)/dtc/libfdt
|
||||
DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt
|
||||
|
||||
.PHONY: dtc/all
|
||||
dtc/all: .git-submodule-status dtc/libfdt
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt,)
|
||||
dtc/all: .git-submodule-status dtc/libfdt dtc/tests
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
|
||||
|
||||
dtc/%: .git-submodule-status
|
||||
@mkdir -p $@
|
||||
@@ -556,16 +500,15 @@ capstone/all: .git-submodule-status
|
||||
|
||||
.PHONY: slirp/all
|
||||
slirp/all: .git-submodule-status
|
||||
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp \
|
||||
BUILD_DIR="$(BUILD_DIR)/slirp" \
|
||||
PKG_CONFIG="$(PKG_CONFIG)" \
|
||||
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
|
||||
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
|
||||
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(LDFLAGS)")
|
||||
|
||||
# Compatibility gunk to keep make working across the rename of targets
|
||||
# for recursion, to be removed some time after 4.1.
|
||||
subdir-dtc: dtc/all
|
||||
subdir-capstone: capstone/all
|
||||
subdir-slirp: slirp/all
|
||||
|
||||
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y)
|
||||
|
||||
$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
|
||||
|
||||
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
|
||||
@@ -601,7 +544,6 @@ qemu-img.o: qemu-img-cmds.h
|
||||
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
||||
|
||||
@@ -610,6 +552,7 @@ qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
||||
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
ifdef CONFIG_MPATH
|
||||
@@ -625,26 +568,18 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
|
||||
qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS)
|
||||
qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
|
||||
|
||||
qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
|
||||
$(SRC_PATH)/scripts/qapi/commands.py \
|
||||
$(SRC_PATH)/scripts/qapi/common.py \
|
||||
$(SRC_PATH)/scripts/qapi/doc.py \
|
||||
$(SRC_PATH)/scripts/qapi/error.py \
|
||||
qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \
|
||||
$(SRC_PATH)/scripts/qapi/events.py \
|
||||
$(SRC_PATH)/scripts/qapi/expr.py \
|
||||
$(SRC_PATH)/scripts/qapi/gen.py \
|
||||
$(SRC_PATH)/scripts/qapi/introspect.py \
|
||||
$(SRC_PATH)/scripts/qapi/parser.py \
|
||||
$(SRC_PATH)/scripts/qapi/schema.py \
|
||||
$(SRC_PATH)/scripts/qapi/source.py \
|
||||
$(SRC_PATH)/scripts/qapi/types.py \
|
||||
$(SRC_PATH)/scripts/qapi/visit.py \
|
||||
$(SRC_PATH)/scripts/qapi/common.py \
|
||||
$(SRC_PATH)/scripts/qapi/doc.py \
|
||||
$(SRC_PATH)/scripts/qapi-gen.py
|
||||
|
||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
|
||||
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \
|
||||
qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
|
||||
qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c \
|
||||
qga/qapi-generated/qga-qapi-doc.texi: \
|
||||
qga/qapi-generated/qapi-gen-timestamp ;
|
||||
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
|
||||
@@ -663,18 +598,7 @@ qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
|
||||
"GEN","$(@:%-timestamp=%)")
|
||||
@>$@
|
||||
|
||||
qapi-modules-storage-daemon = \
|
||||
$(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \
|
||||
$(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json)
|
||||
|
||||
$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ;
|
||||
storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
||||
-o "storage-daemon/qapi" $<, \
|
||||
"GEN","$(@:%-timestamp=%)")
|
||||
@>$@
|
||||
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h)
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h)
|
||||
$(qga-obj-y): $(QGALIB_GEN)
|
||||
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS)
|
||||
@@ -720,13 +644,7 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad"
|
||||
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
|
||||
# relies on Linux-specific syscalls
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
endif
|
||||
|
||||
vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a
|
||||
vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
ifdef CONFIG_VHOST_USER_INPUT
|
||||
@@ -763,7 +681,7 @@ clean: recurse-clean
|
||||
! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
|
||||
-exec rm {} +
|
||||
rm -f $(edk2-decompressed)
|
||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) TAGS cscope.* *.pod *~ */*~
|
||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga$(EXESUF) TAGS cscope.* *.pod *~ */*~
|
||||
rm -f fsdev/*.pod scsi/*.pod
|
||||
rm -f qemu-img-cmds.h
|
||||
rm -f ui/shader/*-vert.h ui/shader/*-frag.h
|
||||
@@ -772,10 +690,8 @@ clean: recurse-clean
|
||||
rm -f trace/generated-tracers-dtrace.h*
|
||||
rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp)
|
||||
rm -f qapi-gen-timestamp
|
||||
rm -f storage-daemon/qapi/qapi-gen-timestamp
|
||||
rm -rf qga/qapi-generated
|
||||
rm -f config-all-devices.mak
|
||||
rm -f $(SUBDIR_DEVICES_MAK)
|
||||
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
|
||||
@@ -784,19 +700,32 @@ dist: qemu-$(VERSION).tar.bz2
|
||||
qemu-%.tar.bz2:
|
||||
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
|
||||
|
||||
# Sphinx does not allow building manuals into the same directory as
|
||||
# the source files, so if we're doing an in-tree QEMU build we must
|
||||
# build the manuals into a subdirectory (and then install them from
|
||||
# there for 'make install'). For an out-of-tree build we can just
|
||||
# use the docs/ subdirectory in the build tree as normal.
|
||||
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
|
||||
MANUAL_BUILDDIR := docs/built
|
||||
else
|
||||
MANUAL_BUILDDIR := docs
|
||||
endif
|
||||
|
||||
define clean-manual =
|
||||
rm -rf $(MANUAL_BUILDDIR)/$1/_static
|
||||
rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html
|
||||
endef
|
||||
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h* $(DOCS)
|
||||
rm -f tests/tcg/config-*.mak
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||
rm -f $(SUBDIR_DEVICES_MAK)
|
||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
|
||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
||||
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
|
||||
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
|
||||
rm -f qemu-doc.vr qemu-doc.txt
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
rm -f docs/version.texi
|
||||
@@ -805,17 +734,17 @@ distclean: clean
|
||||
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
rm -f docs/qemu-block-drivers.7
|
||||
rm -f docs/qemu-cpu-models.7
|
||||
rm -rf .doctrees
|
||||
$(call clean-manual,devel)
|
||||
$(call clean-manual,interop)
|
||||
$(call clean-manual,specs)
|
||||
$(call clean-manual,system)
|
||||
$(call clean-manual,tools)
|
||||
$(call clean-manual,user)
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
rm -Rf .sdk
|
||||
if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi
|
||||
|
||||
KEYMAPS=da en-gb et fr fr-ch is lt no pt-br sv \
|
||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
@@ -823,10 +752,10 @@ de-ch es fo fr-ca hu ja mk pt sl tr \
|
||||
bepo cz
|
||||
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \
|
||||
openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
||||
@@ -836,13 +765,13 @@ qemu-nsis.bmp \
|
||||
bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
||||
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \
|
||||
s390-ccw.img s390-netboot.img \
|
||||
slof.bin skiboot.lid \
|
||||
spapr-rtas.bin slof.bin skiboot.lid \
|
||||
palcode-clipper \
|
||||
u-boot.e500 u-boot-sam460-20100605.bin \
|
||||
qemu_vga.ndrv \
|
||||
edk2-licenses.txt \
|
||||
hppa-firmware.img \
|
||||
opensbi-riscv32-sifive_u-fw_jump.bin opensbi-riscv32-virt-fw_jump.bin \
|
||||
opensbi-riscv32-virt-fw_jump.bin \
|
||||
opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin
|
||||
|
||||
|
||||
@@ -854,11 +783,10 @@ DESCS=
|
||||
endif
|
||||
|
||||
# Note that we manually filter-out the non-Sphinx documentation which
|
||||
# is currently built into the docs/interop directory in the build tree,
|
||||
# and also any sphinx-built manpages.
|
||||
# is currently built into the docs/interop directory in the build tree.
|
||||
define install-manual =
|
||||
for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
|
||||
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
|
||||
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
|
||||
endef
|
||||
|
||||
# Note that we deliberately do not install the "devel" manual: it is
|
||||
@@ -867,45 +795,38 @@ endef
|
||||
install-sphinxdocs: sphinxdocs
|
||||
$(call install-manual,interop)
|
||||
$(call install-manual,specs)
|
||||
$(call install-manual,system)
|
||||
$(call install-manual,tools)
|
||||
$(call install-manual,user)
|
||||
|
||||
install-doc: $(DOCS) install-sphinxdocs
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)/interop"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)/interop"
|
||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
ifdef CONFIG_POSIX
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
||||
ifeq ($(CONFIG_TOOLS),y)
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
||||
ifneq ($(TOOLS),)
|
||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
endif
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)/interop"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)/interop"
|
||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_VIRTFS
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtiofsd.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
|
||||
install-datadir:
|
||||
@@ -920,15 +841,11 @@ endif
|
||||
|
||||
ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
|
||||
|
||||
install-includedir:
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(includedir)"
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc) \
|
||||
install-datadir install-localstatedir install-includedir \
|
||||
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir \
|
||||
$(if $(INSTALL_BLOBS),$(edk2-decompressed)) \
|
||||
recurse-install
|
||||
ifneq ($(TOOLS),)
|
||||
$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir))
|
||||
$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
|
||||
endif
|
||||
ifneq ($(CONFIG_MODULES),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
|
||||
@@ -986,9 +903,6 @@ endif
|
||||
"$(DESTDIR)$(qemu_desktopdir)/qemu.desktop"
|
||||
ifdef CONFIG_GTK
|
||||
$(MAKE) -C po $@
|
||||
endif
|
||||
ifeq ($(CONFIG_PLUGIN),y)
|
||||
$(INSTALL_DATA) $(SRC_PATH)/include/qemu/qemu-plugin.h "$(DESTDIR)$(includedir)/qemu-plugin.h"
|
||||
endif
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
||||
set -e; for x in $(KEYMAPS); do \
|
||||
@@ -1060,64 +974,33 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
|
||||
# and handles "don't rebuild things unless necessary" itself.
|
||||
# The '.doctrees' files are cached information to speed this up.
|
||||
.PHONY: sphinxdocs
|
||||
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html \
|
||||
$(MANUAL_BUILDDIR)/interop/index.html \
|
||||
$(MANUAL_BUILDDIR)/specs/index.html \
|
||||
$(MANUAL_BUILDDIR)/system/index.html \
|
||||
$(MANUAL_BUILDDIR)/tools/index.html \
|
||||
$(MANUAL_BUILDDIR)/user/index.html
|
||||
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html $(MANUAL_BUILDDIR)/specs/index.html
|
||||
|
||||
# Canned command to build a single manual
|
||||
# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man')
|
||||
# Note the use of different doctree for each (manual, builder) tuple;
|
||||
# this works around Sphinx not handling parallel invocation on
|
||||
# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946
|
||||
build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" $(SPHINX_BUILD) $(if $(V),,-q) $(SPHINX_WERROR) -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
|
||||
build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -W -n -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
|
||||
# We assume all RST files in the manual's directory are used in it
|
||||
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst $(SRC_PATH)/docs/$1/*/*.rst) \
|
||||
$(SRC_PATH)/docs/defs.rst.inc \
|
||||
$(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py \
|
||||
$(SRC_PATH)/docs/sphinx/*.py
|
||||
# Macro to write out the rule and dependencies for building manpages
|
||||
# Usage: $(call define-manpage-rule,manualname,manpage1 manpage2...[,extradeps])
|
||||
# 'extradeps' is optional, and specifies extra files (eg .hx files) that
|
||||
# the manual page depends on.
|
||||
define define-manpage-rule
|
||||
$(call atomic,$(foreach manpage,$2,$(MANUAL_BUILDDIR)/$1/$(manpage)),$(call manual-deps,$1) $3)
|
||||
$(call build-manual,$1,man)
|
||||
endef
|
||||
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
|
||||
|
||||
$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
|
||||
$(call build-manual,devel,html)
|
||||
$(call build-manual,devel)
|
||||
|
||||
$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
|
||||
$(call build-manual,interop,html)
|
||||
$(call build-manual,interop)
|
||||
|
||||
$(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
|
||||
$(call build-manual,specs,html)
|
||||
$(call build-manual,specs)
|
||||
|
||||
$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx
|
||||
$(call build-manual,system,html)
|
||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
|
||||
$(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc
|
||||
$(call build-manual,tools,html)
|
||||
qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
|
||||
$(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user)
|
||||
$(call build-manual,user,html)
|
||||
qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
|
||||
$(call define-manpage-rule,interop,qemu-ga.8)
|
||||
|
||||
$(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7)
|
||||
|
||||
$(call define-manpage-rule,tools,\
|
||||
qemu-img.1 qemu-nbd.8 qemu-trace-stap.1\
|
||||
virtiofsd.1 virtfs-proxy-helper.1,\
|
||||
$(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc)
|
||||
|
||||
$(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h
|
||||
@mkdir -p "$(MANUAL_BUILDDIR)"
|
||||
$(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \
|
||||
"GEN","$@")
|
||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
|
||||
docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
|
||||
@cp -p $< $@
|
||||
@@ -1125,10 +1008,27 @@ docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
|
||||
docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi
|
||||
@cp -p $< $@
|
||||
|
||||
html: docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
|
||||
info: docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||
pdf: docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
txt: docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
qemu.1: qemu-option-trace.texi
|
||||
qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi
|
||||
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||
qemu-ga.8: qemu-ga.texi
|
||||
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
||||
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
|
||||
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
|
||||
|
||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
|
||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
|
||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi \
|
||||
qemu-tech.texi qemu-option-trace.texi \
|
||||
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
|
||||
docs/qemu-cpu-models.texi docs/security.texi
|
||||
|
||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||
@@ -1231,66 +1131,50 @@ endif
|
||||
include $(SRC_PATH)/tests/docker/Makefile.include
|
||||
include $(SRC_PATH)/tests/vm/Makefile.include
|
||||
|
||||
print-help-run = printf " %-30s - %s\\n" "$1" "$2"
|
||||
print-help = $(quiet-@)$(call print-help-run,$1,$2)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo 'Generic targets:'
|
||||
$(call print-help,all,Build all)
|
||||
@echo ' all - Build all'
|
||||
ifdef CONFIG_MODULES
|
||||
$(call print-help,modules,Build all modules)
|
||||
@echo ' modules - Build all modules'
|
||||
endif
|
||||
$(call print-help,dir/file.o,Build specified target only)
|
||||
$(call print-help,install,Install QEMU, documentation and tools)
|
||||
$(call print-help,ctags/TAGS,Generate tags file for editors)
|
||||
$(call print-help,cscope,Generate cscope index)
|
||||
@echo ' dir/file.o - Build specified target only'
|
||||
@echo ' install - Install QEMU, documentation and tools'
|
||||
@echo ' ctags/TAGS - Generate tags file for editors'
|
||||
@echo ' cscope - Generate cscope index'
|
||||
@echo ''
|
||||
@$(if $(TARGET_DIRS), \
|
||||
echo 'Architecture specific targets:'; \
|
||||
$(foreach t, $(TARGET_DIRS), \
|
||||
$(call print-help-run,$(t)/all,Build for $(t)); \
|
||||
$(if $(CONFIG_FUZZ), \
|
||||
$(if $(findstring softmmu,$(t)), \
|
||||
$(call print-help-run,$(t)/fuzz,Build fuzzer for $(t)); \
|
||||
))) \
|
||||
echo '')
|
||||
@$(if $(HELPERS-y), \
|
||||
echo 'Helper targets:'; \
|
||||
$(foreach t, $(HELPERS-y), \
|
||||
$(call print-help-run,$(t),Build $(shell basename $(t)));) \
|
||||
echo '')
|
||||
@$(if $(TOOLS), \
|
||||
echo 'Tools targets:'; \
|
||||
$(foreach t, $(TOOLS), \
|
||||
$(call print-help-run,$(t),Build $(shell basename $(t)) tool);) \
|
||||
printf " %-30s - Build for %s\\n" $(t)/all $(t);) \
|
||||
echo '')
|
||||
@echo 'Cleaning targets:'
|
||||
$(call print-help,clean,Remove most generated files but keep the config)
|
||||
@echo ' clean - Remove most generated files but keep the config'
|
||||
ifdef CONFIG_GCOV
|
||||
$(call print-help,clean-coverage,Remove coverage files)
|
||||
@echo ' clean-coverage - Remove coverage files'
|
||||
endif
|
||||
$(call print-help,distclean,Remove all generated files)
|
||||
$(call print-help,dist,Build a distributable tarball)
|
||||
@echo ' distclean - Remove all generated files'
|
||||
@echo ' dist - Build a distributable tarball'
|
||||
@echo ''
|
||||
@echo 'Test targets:'
|
||||
$(call print-help,check,Run all tests (check-help for details))
|
||||
$(call print-help,docker,Help about targets running tests inside containers)
|
||||
$(call print-help,vm-help,Help about targets running tests inside VM)
|
||||
@echo ' check - Run all tests (check-help for details)'
|
||||
@echo ' docker - Help about targets running tests inside Docker containers'
|
||||
@echo ' vm-help - Help about targets running tests inside VM'
|
||||
@echo ''
|
||||
@echo 'Documentation targets:'
|
||||
$(call print-help,html info pdf txt,Build documentation in specified format)
|
||||
@echo ' html info pdf txt'
|
||||
@echo ' - Build documentation in specified format'
|
||||
ifdef CONFIG_GCOV
|
||||
$(call print-help,coverage-report,Create code coverage report)
|
||||
@echo ' coverage-report - Create code coverage report'
|
||||
endif
|
||||
@echo ''
|
||||
ifdef CONFIG_WIN32
|
||||
@echo 'Windows targets:'
|
||||
$(call print-help,installer,Build NSIS-based installer for QEMU)
|
||||
@echo ' installer - Build NSIS-based installer for QEMU'
|
||||
ifdef QEMU_GA_MSI_ENABLED
|
||||
$(call print-help,msi,Build MSI-based installer for qemu-ga)
|
||||
@echo ' msi - Build MSI-based installer for qemu-ga'
|
||||
endif
|
||||
@echo ''
|
||||
endif
|
||||
$(call print-help,$(MAKE) [targets],(quiet build, default))
|
||||
$(call print-help,$(MAKE) V=1 [targets],(verbose build))
|
||||
@echo ' $(MAKE) [targets] (quiet build, default)'
|
||||
@echo ' $(MAKE) V=1 [targets] (verbose build)'
|
||||
+31
-33
@@ -1,39 +1,41 @@
|
||||
#######################################################################
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/
|
||||
util-obj-y = crypto/ util/ qobject/ qapi/
|
||||
qom-obj-y = qom/
|
||||
|
||||
#######################################################################
|
||||
# code used by both qemu system emulation and qemu-img
|
||||
|
||||
ifeq ($(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS)),y)
|
||||
stub-obj-y = stubs/ util/ crypto/
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
|
||||
chardev-obj-y = chardev/
|
||||
|
||||
#######################################################################
|
||||
# authz-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
authz-obj-y = authz/
|
||||
|
||||
block-obj-y = block/ nbd/ scsi/
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y = nbd/
|
||||
block-obj-y += block.o blockjob.o job.o
|
||||
block-obj-y += block/ scsi/
|
||||
block-obj-y += qemu-io-cmds.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
|
||||
block-obj-m = block/
|
||||
|
||||
#######################################################################
|
||||
# crypto-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
crypto-obj-y = crypto/
|
||||
|
||||
io-obj-y = io/
|
||||
|
||||
endif # CONFIG_SOFTMMU or CONFIG_TOOLS
|
||||
crypto-user-obj-y = crypto/
|
||||
|
||||
#######################################################################
|
||||
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
|
||||
# used for system emulation, too, but specified separately there)
|
||||
# qom-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/
|
||||
storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o
|
||||
storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
qom-obj-y = qom/
|
||||
|
||||
#######################################################################
|
||||
# io-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
io-obj-y = io/
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
@@ -47,31 +49,32 @@ common-obj-y += dump/
|
||||
common-obj-y += job-qmp.o
|
||||
common-obj-y += monitor/
|
||||
common-obj-y += net/
|
||||
common-obj-y += qdev-monitor.o
|
||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += accel/
|
||||
common-obj-y += migration/
|
||||
|
||||
common-obj-y += audio/
|
||||
common-obj-m += audio/
|
||||
common-obj-y += hw/
|
||||
common-obj-m += hw/
|
||||
|
||||
common-obj-y += replay/
|
||||
|
||||
common-obj-y += ui/
|
||||
common-obj-m += ui/
|
||||
common-obj-y += bt-host.o bt-vhci.o
|
||||
bt-host.o-cflags := $(BLUEZ_CFLAGS)
|
||||
|
||||
common-obj-y += dma-helpers.o
|
||||
common-obj-y += vl.o
|
||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += backends/
|
||||
common-obj-y += chardev/
|
||||
common-obj-m += chardev/
|
||||
|
||||
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
|
||||
qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS)
|
||||
@@ -79,9 +82,11 @@ qemu-seccomp.o-libs := $(SECCOMP_LIBS)
|
||||
|
||||
common-obj-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
common-obj-y += qapi/
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
common-obj-y += qapi/
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
@@ -120,14 +125,11 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/
|
||||
rdmacm-mux-obj-y = contrib/rdmacm-mux/
|
||||
vhost-user-input-obj-y = contrib/vhost-user-input/
|
||||
vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
|
||||
virtiofsd-obj-y = tools/virtiofsd/
|
||||
|
||||
######################################################################
|
||||
trace-events-subdirs =
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += backends
|
||||
trace-events-subdirs += backends/tpm
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += monitor
|
||||
ifeq ($(CONFIG_USER_ONLY),y)
|
||||
@@ -141,8 +143,8 @@ trace-events-subdirs += nbd
|
||||
trace-events-subdirs += scsi
|
||||
endif
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += chardev
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/alpha
|
||||
@@ -153,7 +155,6 @@ trace-events-subdirs += hw/block/dataplane
|
||||
trace-events-subdirs += hw/char
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/hyperv
|
||||
trace-events-subdirs += hw/i2c
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
@@ -172,13 +173,11 @@ trace-events-subdirs += hw/pci-host
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/rdma
|
||||
trace-events-subdirs += hw/rdma/vmw
|
||||
trace-events-subdirs += hw/rtc
|
||||
trace-events-subdirs += hw/s390x
|
||||
trace-events-subdirs += hw/scsi
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/ssi
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += hw/usb
|
||||
@@ -192,7 +191,6 @@ trace-events-subdirs += migration
|
||||
trace-events-subdirs += net
|
||||
trace-events-subdirs += ui
|
||||
endif
|
||||
trace-events-subdirs += hw/core
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += qom
|
||||
|
||||
+13
-39
@@ -12,7 +12,7 @@ endif
|
||||
|
||||
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -isystem ../linux-headers
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
endif
|
||||
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
|
||||
@@ -39,6 +39,9 @@ endif
|
||||
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
||||
STPFILES=
|
||||
|
||||
# Makefile Tests
|
||||
include $(SRC_PATH)/tests/tcg/Makefile.include
|
||||
|
||||
config-target.h: config-target.h-timestamp
|
||||
config-target.h-timestamp: config-target.mak
|
||||
|
||||
@@ -107,7 +110,7 @@ obj-y += trace/
|
||||
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
obj-y += exec.o exec-vary.o
|
||||
obj-y += exec.o
|
||||
obj-y += accel/
|
||||
obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
|
||||
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
|
||||
@@ -119,8 +122,6 @@ obj-y += disas.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
LIBS := $(libs_cpu) $(LIBS)
|
||||
|
||||
obj-$(CONFIG_PLUGIN) += plugins/
|
||||
|
||||
#########################################################
|
||||
# Linux user emulator target
|
||||
|
||||
@@ -128,8 +129,7 @@ ifdef CONFIG_LINUX_USER
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
|
||||
-I$(SRC_PATH)/linux-user/host/$(ARCH) \
|
||||
-I$(SRC_PATH)/linux-user \
|
||||
-Ilinux-user/$(TARGET_ABI_DIR)
|
||||
-I$(SRC_PATH)/linux-user
|
||||
|
||||
obj-y += linux-user/
|
||||
obj-y += gdbstub.o thunk.o
|
||||
@@ -152,12 +152,14 @@ endif #CONFIG_BSD_USER
|
||||
#########################################################
|
||||
# System emulator target
|
||||
ifdef CONFIG_SOFTMMU
|
||||
obj-y += softmmu/
|
||||
obj-y += gdbstub.o
|
||||
obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o
|
||||
obj-y += qtest.o
|
||||
obj-y += dump/
|
||||
obj-y += hw/
|
||||
obj-y += monitor/
|
||||
obj-y += qapi/
|
||||
obj-y += memory.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += migration/ram.o
|
||||
LIBS := $(libs_softmmu) $(LIBS)
|
||||
|
||||
@@ -176,20 +178,6 @@ endif # CONFIG_SOFTMMU
|
||||
dummy := $(call unnest-vars,,obj-y)
|
||||
all-obj-y := $(obj-y)
|
||||
|
||||
#
|
||||
# common-obj-m has some crap here, probably as side effect from
|
||||
# unnest-vars recursing into target directories to fill obj-y and not
|
||||
# properly handling the -m case.
|
||||
#
|
||||
# Clear common-obj-m as workaround. Fixes suspious dependency errors
|
||||
# when building devices as modules. A bit hackish, but should be ok
|
||||
# as long as we do not have any target-specific modules.
|
||||
#
|
||||
# The meson-based build system currently in development doesn't need
|
||||
# unnest-vars and will obsolete this workaround.
|
||||
#
|
||||
common-obj-m :=
|
||||
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
dummy := $(call unnest-vars,.., \
|
||||
authz-obj-y \
|
||||
@@ -197,6 +185,7 @@ dummy := $(call unnest-vars,.., \
|
||||
block-obj-m \
|
||||
chardev-obj-y \
|
||||
crypto-obj-y \
|
||||
crypto-user-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
@@ -205,6 +194,7 @@ all-obj-y += $(common-obj-y)
|
||||
all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
||||
@@ -215,7 +205,7 @@ endif
|
||||
COMMON_LDADDS = ../libqemuutil.a
|
||||
|
||||
# build either PROG or PROGW
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y)
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
ifdef CONFIG_DARWIN
|
||||
$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
|
||||
@@ -240,22 +230,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
rm -f *.stp
|
||||
endif
|
||||
|
||||
ifdef CONFIG_FUZZ
|
||||
include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include
|
||||
include $(SRC_PATH)/tests/qtest/Makefile.include
|
||||
|
||||
fuzz: fuzz-vars
|
||||
fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
|
||||
fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
|
||||
fuzz-vars: $(QEMU_PROG_FUZZ)
|
||||
dummy := $(call unnest-vars,, fuzz-obj-y)
|
||||
|
||||
|
||||
$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
|
||||
endif
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
$(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
|
||||
|
||||
+18
-29
@@ -1,6 +1,5 @@
|
||||
===========
|
||||
QEMU README
|
||||
===========
|
||||
QEMU README
|
||||
===========
|
||||
|
||||
QEMU is a generic and open source machine & userspace emulator and
|
||||
virtualizer.
|
||||
@@ -38,9 +37,6 @@ QEMU is multi-platform software intended to be buildable on all modern
|
||||
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
|
||||
of other UNIX targets. The simple steps to build QEMU are:
|
||||
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
@@ -48,9 +44,9 @@ of other UNIX targets. The simple steps to build QEMU are:
|
||||
|
||||
Additional information can also be found online via the QEMU website:
|
||||
|
||||
* `<https://qemu.org/Hosts/Linux>`_
|
||||
* `<https://qemu.org/Hosts/Mac>`_
|
||||
* `<https://qemu.org/Hosts/W32>`_
|
||||
https://qemu.org/Hosts/Linux
|
||||
https://qemu.org/Hosts/Mac
|
||||
https://qemu.org/Hosts/W32
|
||||
|
||||
|
||||
Submitting patches
|
||||
@@ -58,29 +54,24 @@ Submitting patches
|
||||
|
||||
The QEMU source code is maintained under the GIT version control system.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://git.qemu.org/git/qemu.git
|
||||
|
||||
When submitting patches, one common approach is to use 'git
|
||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||
qemu-devel@nongnu.org mailing list. All patches submitted must contain
|
||||
a 'Signed-off-by' line from the author. Patches should follow the
|
||||
guidelines set out in the CODING_STYLE.rst file.
|
||||
guidelines set out in the HACKING and CODING_STYLE files.
|
||||
|
||||
Additional information on submitting patches can be found online via
|
||||
the QEMU website
|
||||
|
||||
* `<https://qemu.org/Contribute/SubmitAPatch>`_
|
||||
* `<https://qemu.org/Contribute/TrivialPatches>`_
|
||||
https://qemu.org/Contribute/SubmitAPatch
|
||||
https://qemu.org/Contribute/TrivialPatches
|
||||
|
||||
The QEMU website is also maintained under source control.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://git.qemu.org/git/qemu-web.git
|
||||
|
||||
* `<https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/>`_
|
||||
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
||||
|
||||
A 'git-publish' utility was created to make above process less
|
||||
cumbersome, and is highly recommended for making regular contributions,
|
||||
@@ -91,12 +82,10 @@ manually for once.
|
||||
|
||||
For installation instructions, please go to
|
||||
|
||||
* `<https://github.com/stefanha/git-publish>`_
|
||||
https://github.com/stefanha/git-publish
|
||||
|
||||
The workflow with 'git-publish' is:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git checkout master -b my-feature
|
||||
$ # work on new commits, add your 'Signed-off-by' lines to each
|
||||
$ git publish
|
||||
@@ -106,8 +95,6 @@ back to it in the future.
|
||||
|
||||
Sending v2:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git checkout my-feature # same topic branch
|
||||
$ # making changes to the commits (using 'git rebase', for example)
|
||||
$ git publish
|
||||
@@ -122,7 +109,7 @@ The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs
|
||||
found when running code built from QEMU git or upstream released sources
|
||||
should be reported via:
|
||||
|
||||
* `<https://bugs.launchpad.net/qemu/>`_
|
||||
https://bugs.launchpad.net/qemu/
|
||||
|
||||
If using QEMU via an operating system vendor pre-built binary package, it
|
||||
is preferable to report bugs to the vendor's own bug tracker first. If
|
||||
@@ -131,7 +118,7 @@ reported via launchpad.
|
||||
|
||||
For additional information on bug reporting consult:
|
||||
|
||||
* `<https://qemu.org/Contribute/ReportABug>`_
|
||||
https://qemu.org/Contribute/ReportABug
|
||||
|
||||
|
||||
Contact
|
||||
@@ -140,11 +127,13 @@ Contact
|
||||
The QEMU community can be contacted in a number of ways, with the two
|
||||
main methods being email and IRC
|
||||
|
||||
* `<mailto:qemu-devel@nongnu.org>`_
|
||||
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_
|
||||
* #qemu on irc.oftc.net
|
||||
- qemu-devel@nongnu.org
|
||||
https://lists.nongnu.org/mailman/listinfo/qemu-devel
|
||||
- #qemu on irc.oftc.net
|
||||
|
||||
Information on additional methods of contacting the community can be
|
||||
found online via the QEMU website:
|
||||
|
||||
* `<https://qemu.org/Contribute/StartHere>`_
|
||||
https://qemu.org/Contribute/StartHere
|
||||
|
||||
-- End
|
||||
@@ -1,9 +0,0 @@
|
||||
config TCG
|
||||
bool
|
||||
|
||||
config KVM
|
||||
bool
|
||||
|
||||
config XEN
|
||||
bool
|
||||
select FSDEV_9P if VIRTFS
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
common-obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
|
||||
obj-$(CONFIG_KVM) += kvm/
|
||||
obj-$(CONFIG_TCG) += tcg/
|
||||
obj-$(CONFIG_XEN) += xen/
|
||||
obj-y += stubs/
|
||||
+67
-5
@@ -28,7 +28,13 @@
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "qom/object.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static const TypeInfo accel_type = {
|
||||
.name = TYPE_ACCEL,
|
||||
@@ -38,7 +44,7 @@ static const TypeInfo accel_type = {
|
||||
};
|
||||
|
||||
/* Lookup AccelClass from opt_name. Returns NULL if not found */
|
||||
AccelClass *accel_find(const char *opt_name)
|
||||
static AccelClass *accel_find(const char *opt_name)
|
||||
{
|
||||
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
|
||||
AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
|
||||
@@ -46,9 +52,11 @@ AccelClass *accel_find(const char *opt_name)
|
||||
return ac;
|
||||
}
|
||||
|
||||
int accel_init_machine(AccelState *accel, MachineState *ms)
|
||||
static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
||||
{
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
ObjectClass *oc = OBJECT_CLASS(acc);
|
||||
const char *cname = object_class_get_name(oc);
|
||||
AccelState *accel = ACCEL(object_new(cname));
|
||||
int ret;
|
||||
ms->accelerator = accel;
|
||||
*(acc->allowed) = true;
|
||||
@@ -63,9 +71,63 @@ int accel_init_machine(AccelState *accel, MachineState *ms)
|
||||
return ret;
|
||||
}
|
||||
|
||||
AccelState *current_accel(void)
|
||||
void configure_accelerator(MachineState *ms, const char *progname)
|
||||
{
|
||||
return current_machine->accelerator;
|
||||
const char *accel;
|
||||
char **accel_list, **tmp;
|
||||
int ret;
|
||||
bool accel_initialised = false;
|
||||
bool init_failed = false;
|
||||
AccelClass *acc = NULL;
|
||||
|
||||
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
|
||||
if (accel == NULL) {
|
||||
/* Select the default accelerator */
|
||||
int pnlen = strlen(progname);
|
||||
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
|
||||
/* If the program name ends with "kvm", we prefer KVM */
|
||||
accel = "kvm:tcg";
|
||||
} else {
|
||||
#if defined(CONFIG_TCG)
|
||||
accel = "tcg";
|
||||
#elif defined(CONFIG_KVM)
|
||||
accel = "kvm";
|
||||
#else
|
||||
error_report("No accelerator selected and"
|
||||
" no default accelerator available");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
accel_list = g_strsplit(accel, ":", 0);
|
||||
|
||||
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
|
||||
acc = accel_find(*tmp);
|
||||
if (!acc) {
|
||||
continue;
|
||||
}
|
||||
ret = accel_init_machine(acc, ms);
|
||||
if (ret < 0) {
|
||||
init_failed = true;
|
||||
error_report("failed to initialize %s: %s",
|
||||
acc->name, strerror(-ret));
|
||||
} else {
|
||||
accel_initialised = true;
|
||||
}
|
||||
}
|
||||
g_strfreev(accel_list);
|
||||
|
||||
if (!accel_initialised) {
|
||||
if (!init_failed) {
|
||||
error_report("-machine accel=%s: No accelerator found", accel);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (init_failed) {
|
||||
error_report("Back to %s accelerator", acc->name);
|
||||
}
|
||||
}
|
||||
|
||||
void accel_setup_post(MachineState *ms)
|
||||
|
||||
+135
-463
@@ -23,27 +23,22 @@
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "hw/s390x/adapter.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/kvm_int.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sev.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/qapi-types-common.h"
|
||||
#include "qapi/qapi-visit-common.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/balloon.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
@@ -55,7 +50,7 @@
|
||||
/* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We
|
||||
* need to use the real host PAGE_SIZE, as that's what KVM will use.
|
||||
*/
|
||||
#define PAGE_SIZE qemu_real_host_page_size
|
||||
#define PAGE_SIZE getpagesize()
|
||||
|
||||
//#define DEBUG_KVM
|
||||
|
||||
@@ -95,12 +90,8 @@ struct KVMState
|
||||
int max_nested_state_len;
|
||||
int many_ioeventfds;
|
||||
int intx_set_mask;
|
||||
int kvm_shadow_mem;
|
||||
bool kernel_irqchip_allowed;
|
||||
bool kernel_irqchip_required;
|
||||
OnOffAuto kernel_irqchip_split;
|
||||
bool sync_mmu;
|
||||
uint64_t manual_dirty_log_protect;
|
||||
bool manual_dirty_log_protect;
|
||||
/* The man page (and posix) say ioctl numbers are signed int, but
|
||||
* they're not. Linux, glibc and *BSD all treat ioctl numbers as
|
||||
* unsigned, and treating them as signed here can break things */
|
||||
@@ -147,7 +138,6 @@ bool kvm_direct_msi_allowed;
|
||||
bool kvm_ioeventfd_any_length_allowed;
|
||||
bool kvm_msi_use_devid;
|
||||
static bool kvm_immediate_exit;
|
||||
static hwaddr kvm_max_slot_size = ~0;
|
||||
|
||||
static const KVMCapabilityInfo kvm_required_capabilites[] = {
|
||||
KVM_CAP_INFO(USER_MEMORY),
|
||||
@@ -156,65 +146,12 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
|
||||
static NotifierList kvm_irqchip_change_notifiers =
|
||||
NOTIFIER_LIST_INITIALIZER(kvm_irqchip_change_notifiers);
|
||||
|
||||
struct KVMResampleFd {
|
||||
int gsi;
|
||||
EventNotifier *resample_event;
|
||||
QLIST_ENTRY(KVMResampleFd) node;
|
||||
};
|
||||
typedef struct KVMResampleFd KVMResampleFd;
|
||||
|
||||
/*
|
||||
* Only used with split irqchip where we need to do the resample fd
|
||||
* kick for the kernel from userspace.
|
||||
*/
|
||||
static QLIST_HEAD(, KVMResampleFd) kvm_resample_fd_list =
|
||||
QLIST_HEAD_INITIALIZER(kvm_resample_fd_list);
|
||||
|
||||
#define kvm_slots_lock(kml) qemu_mutex_lock(&(kml)->slots_lock)
|
||||
#define kvm_slots_unlock(kml) qemu_mutex_unlock(&(kml)->slots_lock)
|
||||
|
||||
static inline void kvm_resample_fd_remove(int gsi)
|
||||
{
|
||||
KVMResampleFd *rfd;
|
||||
|
||||
QLIST_FOREACH(rfd, &kvm_resample_fd_list, node) {
|
||||
if (rfd->gsi == gsi) {
|
||||
QLIST_REMOVE(rfd, node);
|
||||
g_free(rfd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void kvm_resample_fd_insert(int gsi, EventNotifier *event)
|
||||
{
|
||||
KVMResampleFd *rfd = g_new0(KVMResampleFd, 1);
|
||||
|
||||
rfd->gsi = gsi;
|
||||
rfd->resample_event = event;
|
||||
|
||||
QLIST_INSERT_HEAD(&kvm_resample_fd_list, rfd, node);
|
||||
}
|
||||
|
||||
void kvm_resample_fd_notify(int gsi)
|
||||
{
|
||||
KVMResampleFd *rfd;
|
||||
|
||||
QLIST_FOREACH(rfd, &kvm_resample_fd_list, node) {
|
||||
if (rfd->gsi == gsi) {
|
||||
event_notifier_set(rfd->resample_event);
|
||||
trace_kvm_resample_fd_notify(gsi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_get_max_memslots(void)
|
||||
{
|
||||
KVMState *s = KVM_STATE(current_accel());
|
||||
KVMState *s = KVM_STATE(current_machine->accelerator);
|
||||
|
||||
return s->nr_slots;
|
||||
}
|
||||
@@ -358,23 +295,13 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
|
||||
/* Set the slot size to 0 before setting the slot to the desired
|
||||
* value. This is needed based on KVM commit 75d61fbc. */
|
||||
mem.memory_size = 0;
|
||||
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
}
|
||||
mem.memory_size = slot->memory_size;
|
||||
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
slot->old_flags = mem.flags;
|
||||
err:
|
||||
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
|
||||
mem.memory_size, mem.userspace_addr, ret);
|
||||
if (ret < 0) {
|
||||
error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d,"
|
||||
" start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s",
|
||||
__func__, mem.slot, slot->start_addr,
|
||||
(uint64_t)mem.memory_size, strerror(errno));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -508,7 +435,7 @@ static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
|
||||
static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hwaddr start_addr, size, slot_size;
|
||||
hwaddr start_addr, size;
|
||||
KVMSlot *mem;
|
||||
int ret = 0;
|
||||
|
||||
@@ -519,19 +446,14 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
while (size && !ret) {
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
|
||||
if (!mem) {
|
||||
/* We don't have a slot if we want to trap every access. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kvm_slot_update_flags(kml, mem, section->mr);
|
||||
start_addr += slot_size;
|
||||
size -= slot_size;
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, size);
|
||||
if (!mem) {
|
||||
/* We don't have a slot if we want to trap every access. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kvm_slot_update_flags(kml, mem, section->mr);
|
||||
|
||||
out:
|
||||
kvm_slots_unlock(kml);
|
||||
return ret;
|
||||
@@ -577,7 +499,7 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
||||
{
|
||||
ram_addr_t start = section->offset_within_region +
|
||||
memory_region_get_ram_addr(section->mr);
|
||||
ram_addr_t pages = int128_get64(section->size) / qemu_real_host_page_size;
|
||||
ram_addr_t pages = int128_get64(section->size) / getpagesize();
|
||||
|
||||
cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages);
|
||||
return 0;
|
||||
@@ -585,27 +507,6 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
||||
|
||||
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
|
||||
|
||||
/* Allocate the dirty bitmap for a slot */
|
||||
static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem)
|
||||
{
|
||||
/*
|
||||
* XXX bad kernel interface alert
|
||||
* For dirty bitmap, kernel allocates array of size aligned to
|
||||
* bits-per-long. But for case when the kernel is 64bits and
|
||||
* the userspace is 32bits, userspace can't align to the same
|
||||
* bits-per-long, since sizeof(long) is different between kernel
|
||||
* and user space. This way, userspace will provide buffer which
|
||||
* may be 4 bytes less than the kernel will use, resulting in
|
||||
* userspace memory corruption (which is not detectable by valgrind
|
||||
* too, in most cases).
|
||||
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
|
||||
* a hope that sizeof(long) won't become >8 any time soon.
|
||||
*/
|
||||
hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
|
||||
/*HOST_LONG_BITS*/ 64) / 8;
|
||||
mem->dirty_bmap = g_malloc0(bitmap_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
|
||||
*
|
||||
@@ -624,23 +525,33 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
struct kvm_dirty_log d = {};
|
||||
KVMSlot *mem;
|
||||
hwaddr start_addr, size;
|
||||
hwaddr slot_size, slot_offset = 0;
|
||||
int ret = 0;
|
||||
|
||||
size = kvm_align_section(section, &start_addr);
|
||||
while (size) {
|
||||
MemoryRegionSection subsection = *section;
|
||||
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
|
||||
if (size) {
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, size);
|
||||
if (!mem) {
|
||||
/* We don't have a slot if we want to trap every access. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* XXX bad kernel interface alert
|
||||
* For dirty bitmap, kernel allocates array of size aligned to
|
||||
* bits-per-long. But for case when the kernel is 64bits and
|
||||
* the userspace is 32bits, userspace can't align to the same
|
||||
* bits-per-long, since sizeof(long) is different between kernel
|
||||
* and user space. This way, userspace will provide buffer which
|
||||
* may be 4 bytes less than the kernel will use, resulting in
|
||||
* userspace memory corruption (which is not detectable by valgrind
|
||||
* too, in most cases).
|
||||
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
|
||||
* a hope that sizeof(long) won't become >8 any time soon.
|
||||
*/
|
||||
size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
|
||||
/*HOST_LONG_BITS*/ 64) / 8;
|
||||
if (!mem->dirty_bmap) {
|
||||
/* Allocate on the first log_sync, once and for all */
|
||||
kvm_memslot_init_dirty_bitmap(mem);
|
||||
mem->dirty_bmap = g_malloc0(size);
|
||||
}
|
||||
|
||||
d.dirty_bitmap = mem->dirty_bmap;
|
||||
@@ -651,13 +562,7 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
goto out;
|
||||
}
|
||||
|
||||
subsection.offset_within_region += slot_offset;
|
||||
subsection.size = int128_make64(slot_size);
|
||||
kvm_get_dirty_pages_log_range(&subsection, d.dirty_bitmap);
|
||||
|
||||
slot_offset += slot_size;
|
||||
start_addr += slot_size;
|
||||
size -= slot_size;
|
||||
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
@@ -668,22 +573,63 @@ out:
|
||||
#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT)
|
||||
#define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN)
|
||||
|
||||
static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
|
||||
uint64_t size)
|
||||
/**
|
||||
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
|
||||
*
|
||||
* NOTE: this will be a no-op if we haven't enabled manual dirty log
|
||||
* protection in the host kernel because in that case this operation
|
||||
* will be done within log_sync().
|
||||
*
|
||||
* @kml: the kvm memory listener
|
||||
* @section: the memory range to clear dirty bitmap
|
||||
*/
|
||||
static int kvm_physical_log_clear(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
uint64_t end, bmap_start, start_delta, bmap_npages;
|
||||
struct kvm_clear_dirty_log d;
|
||||
uint64_t start, end, bmap_start, start_delta, bmap_npages, size;
|
||||
unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size;
|
||||
int ret;
|
||||
KVMSlot *mem = NULL;
|
||||
int ret, i;
|
||||
|
||||
if (!s->manual_dirty_log_protect) {
|
||||
/* No need to do explicit clear */
|
||||
return 0;
|
||||
}
|
||||
|
||||
start = section->offset_within_address_space;
|
||||
size = int128_get64(section->size);
|
||||
|
||||
if (!size) {
|
||||
/* Nothing more we can do... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
/* Find any possible slot that covers the section */
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
if (mem->start_addr <= start &&
|
||||
start + size <= mem->start_addr + mem->memory_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We should always find one memslot until this point, otherwise
|
||||
* there could be something wrong from the upper layer
|
||||
*/
|
||||
assert(mem && i != s->nr_slots);
|
||||
|
||||
/*
|
||||
* We need to extend either the start or the size or both to
|
||||
* satisfy the KVM interface requirement. Firstly, do the start
|
||||
* page alignment on 64 host pages
|
||||
*/
|
||||
bmap_start = start & KVM_CLEAR_LOG_MASK;
|
||||
start_delta = start - bmap_start;
|
||||
bmap_start = (start - mem->start_addr) & KVM_CLEAR_LOG_MASK;
|
||||
start_delta = start - mem->start_addr - bmap_start;
|
||||
bmap_start /= psize;
|
||||
|
||||
/*
|
||||
@@ -746,7 +692,7 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
|
||||
/* It should never overflow. If it happens, say something */
|
||||
assert(bmap_npages <= UINT32_MAX);
|
||||
d.num_pages = bmap_npages;
|
||||
d.slot = mem->slot | (as_id << 16);
|
||||
d.slot = mem->slot | (kml->as_id << 16);
|
||||
|
||||
if (kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d) == -1) {
|
||||
ret = -errno;
|
||||
@@ -769,66 +715,6 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
|
||||
size / psize);
|
||||
/* This handles the NULL case well */
|
||||
g_free(bmap_clear);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
|
||||
*
|
||||
* NOTE: this will be a no-op if we haven't enabled manual dirty log
|
||||
* protection in the host kernel because in that case this operation
|
||||
* will be done within log_sync().
|
||||
*
|
||||
* @kml: the kvm memory listener
|
||||
* @section: the memory range to clear dirty bitmap
|
||||
*/
|
||||
static int kvm_physical_log_clear(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
uint64_t start, size, offset, count;
|
||||
KVMSlot *mem;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!s->manual_dirty_log_protect) {
|
||||
/* No need to do explicit clear */
|
||||
return ret;
|
||||
}
|
||||
|
||||
start = section->offset_within_address_space;
|
||||
size = int128_get64(section->size);
|
||||
|
||||
if (!size) {
|
||||
/* Nothing more we can do... */
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
/* Discard slots that are empty or do not overlap the section */
|
||||
if (!mem->memory_size ||
|
||||
mem->start_addr > start + size - 1 ||
|
||||
start > mem->start_addr + mem->memory_size - 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (start >= mem->start_addr) {
|
||||
/* The slot starts before section or is aligned to it. */
|
||||
offset = start - mem->start_addr;
|
||||
count = MIN(mem->memory_size - offset, size);
|
||||
} else {
|
||||
/* The slot starts after section. */
|
||||
offset = 0;
|
||||
count = MIN(mem->memory_size, size - (mem->start_addr - start));
|
||||
}
|
||||
ret = kvm_log_clear_one_slot(mem, kml->as_id, offset, count);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kvm_slots_unlock(kml);
|
||||
|
||||
@@ -933,39 +819,6 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension)
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct HWPoisonPage {
|
||||
ram_addr_t ram_addr;
|
||||
QLIST_ENTRY(HWPoisonPage) list;
|
||||
} HWPoisonPage;
|
||||
|
||||
static QLIST_HEAD(, HWPoisonPage) hwpoison_page_list =
|
||||
QLIST_HEAD_INITIALIZER(hwpoison_page_list);
|
||||
|
||||
static void kvm_unpoison_all(void *param)
|
||||
{
|
||||
HWPoisonPage *page, *next_page;
|
||||
|
||||
QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) {
|
||||
QLIST_REMOVE(page, list);
|
||||
qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE);
|
||||
g_free(page);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_hwpoison_page_add(ram_addr_t ram_addr)
|
||||
{
|
||||
HWPoisonPage *page;
|
||||
|
||||
QLIST_FOREACH(page, &hwpoison_page_list, list) {
|
||||
if (page->ram_addr == ram_addr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
page = g_new(HWPoisonPage, 1);
|
||||
page->ram_addr = ram_addr;
|
||||
QLIST_INSERT_HEAD(&hwpoison_page_list, page, list);
|
||||
}
|
||||
|
||||
static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size)
|
||||
{
|
||||
#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
|
||||
@@ -1098,14 +951,6 @@ kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kvm_set_max_memslot_size(hwaddr max_slot_size)
|
||||
{
|
||||
g_assert(
|
||||
ROUND_UP(max_slot_size, qemu_real_host_page_size) == max_slot_size
|
||||
);
|
||||
kvm_max_slot_size = max_slot_size;
|
||||
}
|
||||
|
||||
static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section, bool add)
|
||||
{
|
||||
@@ -1113,7 +958,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
int err;
|
||||
MemoryRegion *mr = section->mr;
|
||||
bool writeable = !mr->readonly && !mr->rom_device;
|
||||
hwaddr start_addr, size, slot_size;
|
||||
hwaddr start_addr, size;
|
||||
void *ram;
|
||||
|
||||
if (!memory_region_is_ram(mr)) {
|
||||
@@ -1138,59 +983,41 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
if (!add) {
|
||||
do {
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
|
||||
if (!mem) {
|
||||
goto out;
|
||||
}
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
}
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, size);
|
||||
if (!mem) {
|
||||
goto out;
|
||||
}
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
}
|
||||
|
||||
/* unregister the slot */
|
||||
g_free(mem->dirty_bmap);
|
||||
mem->dirty_bmap = NULL;
|
||||
mem->memory_size = 0;
|
||||
mem->flags = 0;
|
||||
err = kvm_set_user_memory_region(kml, mem, false);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error unregistering slot: %s\n",
|
||||
__func__, strerror(-err));
|
||||
abort();
|
||||
}
|
||||
start_addr += slot_size;
|
||||
size -= slot_size;
|
||||
} while (size);
|
||||
/* unregister the slot */
|
||||
g_free(mem->dirty_bmap);
|
||||
mem->dirty_bmap = NULL;
|
||||
mem->memory_size = 0;
|
||||
mem->flags = 0;
|
||||
err = kvm_set_user_memory_region(kml, mem, false);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error unregistering slot: %s\n",
|
||||
__func__, strerror(-err));
|
||||
abort();
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* register the new slot */
|
||||
do {
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_alloc_slot(kml);
|
||||
mem->memory_size = slot_size;
|
||||
mem->start_addr = start_addr;
|
||||
mem->ram = ram;
|
||||
mem->flags = kvm_mem_flags(mr);
|
||||
mem = kvm_alloc_slot(kml);
|
||||
mem->memory_size = size;
|
||||
mem->start_addr = start_addr;
|
||||
mem->ram = ram;
|
||||
mem->flags = kvm_mem_flags(mr);
|
||||
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
/*
|
||||
* Reallocate the bmap; it means it doesn't disappear in
|
||||
* middle of a migrate.
|
||||
*/
|
||||
kvm_memslot_init_dirty_bitmap(mem);
|
||||
}
|
||||
err = kvm_set_user_memory_region(kml, mem, true);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||
strerror(-err));
|
||||
abort();
|
||||
}
|
||||
start_addr += slot_size;
|
||||
ram += slot_size;
|
||||
size -= slot_size;
|
||||
} while (size);
|
||||
err = kvm_set_user_memory_region(kml, mem, true);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||
strerror(-err));
|
||||
abort();
|
||||
}
|
||||
|
||||
out:
|
||||
kvm_slots_unlock(kml);
|
||||
@@ -1513,21 +1340,6 @@ void kvm_irqchip_release_virq(KVMState *s, int virq)
|
||||
trace_kvm_irqchip_release_virq(virq);
|
||||
}
|
||||
|
||||
void kvm_irqchip_add_change_notifier(Notifier *n)
|
||||
{
|
||||
notifier_list_add(&kvm_irqchip_change_notifiers, n);
|
||||
}
|
||||
|
||||
void kvm_irqchip_remove_change_notifier(Notifier *n)
|
||||
{
|
||||
notifier_remove(n);
|
||||
}
|
||||
|
||||
void kvm_irqchip_change_notify(void)
|
||||
{
|
||||
notifier_list_notify(&kvm_irqchip_change_notifiers, NULL);
|
||||
}
|
||||
|
||||
static unsigned int kvm_hash_msi(uint32_t data)
|
||||
{
|
||||
/* This is optimized for IA32 MSI layout. However, no other arch shall
|
||||
@@ -1711,13 +1523,9 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg,
|
||||
return kvm_update_routing_entry(s, &kroute);
|
||||
}
|
||||
|
||||
static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event,
|
||||
EventNotifier *resample, int virq,
|
||||
static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq,
|
||||
bool assign)
|
||||
{
|
||||
int fd = event_notifier_get_fd(event);
|
||||
int rfd = resample ? event_notifier_get_fd(resample) : -1;
|
||||
|
||||
struct kvm_irqfd irqfd = {
|
||||
.fd = fd,
|
||||
.gsi = virq,
|
||||
@@ -1725,33 +1533,8 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event,
|
||||
};
|
||||
|
||||
if (rfd != -1) {
|
||||
assert(assign);
|
||||
if (kvm_irqchip_is_split()) {
|
||||
/*
|
||||
* When the slow irqchip (e.g. IOAPIC) is in the
|
||||
* userspace, KVM kernel resamplefd will not work because
|
||||
* the EOI of the interrupt will be delivered to userspace
|
||||
* instead, so the KVM kernel resamplefd kick will be
|
||||
* skipped. The userspace here mimics what the kernel
|
||||
* provides with resamplefd, remember the resamplefd and
|
||||
* kick it when we receive EOI of this IRQ.
|
||||
*
|
||||
* This is hackery because IOAPIC is mostly bypassed
|
||||
* (except EOI broadcasts) when irqfd is used. However
|
||||
* this can bring much performance back for split irqchip
|
||||
* with INTx IRQs (for VFIO, this gives 93% perf of the
|
||||
* full fast path, which is 46% perf boost comparing to
|
||||
* the INTx slow path).
|
||||
*/
|
||||
kvm_resample_fd_insert(virq, resample);
|
||||
} else {
|
||||
irqfd.flags |= KVM_IRQFD_FLAG_RESAMPLE;
|
||||
irqfd.resamplefd = rfd;
|
||||
}
|
||||
} else if (!assign) {
|
||||
if (kvm_irqchip_is_split()) {
|
||||
kvm_resample_fd_remove(virq);
|
||||
}
|
||||
irqfd.flags |= KVM_IRQFD_FLAG_RESAMPLE;
|
||||
irqfd.resamplefd = rfd;
|
||||
}
|
||||
|
||||
if (!kvm_irqfds_enabled()) {
|
||||
@@ -1847,9 +1630,7 @@ int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event,
|
||||
EventNotifier *resample, int virq,
|
||||
bool assign)
|
||||
static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
@@ -1863,13 +1644,15 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg)
|
||||
int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n,
|
||||
EventNotifier *rn, int virq)
|
||||
{
|
||||
return kvm_irqchip_assign_irqfd(s, n, rn, virq, true);
|
||||
return kvm_irqchip_assign_irqfd(s, event_notifier_get_fd(n),
|
||||
rn ? event_notifier_get_fd(rn) : -1, virq, true);
|
||||
}
|
||||
|
||||
int kvm_irqchip_remove_irqfd_notifier_gsi(KVMState *s, EventNotifier *n,
|
||||
int virq)
|
||||
{
|
||||
return kvm_irqchip_assign_irqfd(s, n, NULL, virq, false);
|
||||
return kvm_irqchip_assign_irqfd(s, event_notifier_get_fd(n), -1, virq,
|
||||
false);
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
||||
@@ -1901,11 +1684,10 @@ void kvm_irqchip_set_qemuirq_gsi(KVMState *s, qemu_irq irq, int gsi)
|
||||
g_hash_table_insert(s->gsimap, irq, GINT_TO_POINTER(gsi));
|
||||
}
|
||||
|
||||
static void kvm_irqchip_create(KVMState *s)
|
||||
static void kvm_irqchip_create(MachineState *machine, KVMState *s)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(s->kernel_irqchip_split != ON_OFF_AUTO_AUTO);
|
||||
if (kvm_check_extension(s, KVM_CAP_IRQCHIP)) {
|
||||
;
|
||||
} else if (kvm_check_extension(s, KVM_CAP_S390_IRQCHIP)) {
|
||||
@@ -1920,9 +1702,9 @@ static void kvm_irqchip_create(KVMState *s)
|
||||
|
||||
/* First probe and see if there's a arch-specific hook to create the
|
||||
* in-kernel irqchip for us */
|
||||
ret = kvm_arch_irqchip_create(s);
|
||||
ret = kvm_arch_irqchip_create(machine, s);
|
||||
if (ret == 0) {
|
||||
if (s->kernel_irqchip_split == ON_OFF_AUTO_ON) {
|
||||
if (machine_kernel_irqchip_split(machine)) {
|
||||
perror("Split IRQ chip mode not supported.");
|
||||
exit(1);
|
||||
} else {
|
||||
@@ -1970,7 +1752,7 @@ static int kvm_max_vcpu_id(KVMState *s)
|
||||
|
||||
bool kvm_vcpu_id_is_valid(int vcpu_id)
|
||||
{
|
||||
KVMState *s = KVM_STATE(current_accel());
|
||||
KVMState *s = KVM_STATE(current_machine->accelerator);
|
||||
return vcpu_id >= 0 && vcpu_id < kvm_max_vcpu_id(s);
|
||||
}
|
||||
|
||||
@@ -1994,7 +1776,6 @@ static int kvm_init(MachineState *ms)
|
||||
int ret;
|
||||
int type = 0;
|
||||
const char *kvm_type;
|
||||
uint64_t dirty_log_manual_caps;
|
||||
|
||||
s = KVM_STATE(ms->accelerator);
|
||||
|
||||
@@ -2004,7 +1785,7 @@ static int kvm_init(MachineState *ms)
|
||||
* even with KVM. TARGET_PAGE_SIZE is assumed to be the minimum
|
||||
* page size for the system though.
|
||||
*/
|
||||
assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size);
|
||||
assert(TARGET_PAGE_SIZE <= getpagesize());
|
||||
|
||||
s->sigmask_len = 8;
|
||||
|
||||
@@ -2120,20 +1901,14 @@ static int kvm_init(MachineState *ms)
|
||||
s->coalesced_pio = s->coalesced_mmio &&
|
||||
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
||||
|
||||
dirty_log_manual_caps =
|
||||
s->manual_dirty_log_protect =
|
||||
kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||
s->manual_dirty_log_protect = dirty_log_manual_caps;
|
||||
if (dirty_log_manual_caps) {
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0,
|
||||
dirty_log_manual_caps);
|
||||
if (s->manual_dirty_log_protect) {
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, 1);
|
||||
if (ret) {
|
||||
warn_report("Trying to enable capability %"PRIu64" of "
|
||||
"KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 but failed. "
|
||||
"Falling back to the legacy mode. ",
|
||||
dirty_log_manual_caps);
|
||||
s->manual_dirty_log_protect = 0;
|
||||
warn_report("Trying to enable KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 "
|
||||
"but failed. Falling back to the legacy mode. ");
|
||||
s->manual_dirty_log_protect = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2200,14 +1975,8 @@ static int kvm_init(MachineState *ms)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
|
||||
s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
|
||||
}
|
||||
|
||||
qemu_register_reset(kvm_unpoison_all, NULL);
|
||||
|
||||
if (s->kernel_irqchip_allowed) {
|
||||
kvm_irqchip_create(s);
|
||||
if (machine_kernel_irqchip_allowed(ms)) {
|
||||
kvm_irqchip_create(ms, s);
|
||||
}
|
||||
|
||||
if (kvm_eventfds_allowed) {
|
||||
@@ -2228,8 +1997,7 @@ static int kvm_init(MachineState *ms)
|
||||
|
||||
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||
if (!s->sync_mmu) {
|
||||
ret = ram_block_discard_disable(true);
|
||||
assert(!ret);
|
||||
qemu_balloon_inhibit(true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2310,9 +2078,9 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
||||
ent = &ring->coalesced_mmio[ring->first];
|
||||
|
||||
if (ent->pio == 1) {
|
||||
address_space_write(&address_space_io, ent->phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||
ent->len);
|
||||
address_space_rw(&address_space_io, ent->phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||
ent->len, true);
|
||||
} else {
|
||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||
}
|
||||
@@ -3089,7 +2857,6 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
|
||||
|
||||
for (i = 0; i < kvm->nr_as; ++i) {
|
||||
if (kvm->as[i].as == as && kvm->as[i].ml) {
|
||||
size = MIN(kvm_max_slot_size, size);
|
||||
return NULL != kvm_lookup_matching_slot(kvm->as[i].ml,
|
||||
start_addr, size);
|
||||
}
|
||||
@@ -3098,88 +2865,6 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void kvm_get_kvm_shadow_mem(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
int64_t value = s->kvm_shadow_mem;
|
||||
|
||||
visit_type_int(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void kvm_set_kvm_shadow_mem(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
int64_t value;
|
||||
|
||||
if (!visit_type_int(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->kvm_shadow_mem = value;
|
||||
}
|
||||
|
||||
static void kvm_set_kernel_irqchip(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
OnOffSplit mode;
|
||||
|
||||
if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case ON_OFF_SPLIT_ON:
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_required = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_OFF;
|
||||
break;
|
||||
case ON_OFF_SPLIT_OFF:
|
||||
s->kernel_irqchip_allowed = false;
|
||||
s->kernel_irqchip_required = false;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_OFF;
|
||||
break;
|
||||
case ON_OFF_SPLIT_SPLIT:
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_required = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_ON;
|
||||
break;
|
||||
default:
|
||||
/* The value was checked in visit_type_OnOffSplit() above. If
|
||||
* we get here, then something is wrong in QEMU.
|
||||
*/
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool kvm_kernel_irqchip_allowed(void)
|
||||
{
|
||||
return kvm_state->kernel_irqchip_allowed;
|
||||
}
|
||||
|
||||
bool kvm_kernel_irqchip_required(void)
|
||||
{
|
||||
return kvm_state->kernel_irqchip_required;
|
||||
}
|
||||
|
||||
bool kvm_kernel_irqchip_split(void)
|
||||
{
|
||||
return kvm_state->kernel_irqchip_split == ON_OFF_AUTO_ON;
|
||||
}
|
||||
|
||||
static void kvm_accel_instance_init(Object *obj)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
|
||||
s->kvm_shadow_mem = -1;
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_AUTO;
|
||||
}
|
||||
|
||||
static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
@@ -3187,24 +2872,11 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
||||
ac->init_machine = kvm_init;
|
||||
ac->has_memory = kvm_accel_has_memory;
|
||||
ac->allowed = &kvm_allowed;
|
||||
|
||||
object_class_property_add(oc, "kernel-irqchip", "on|off|split",
|
||||
NULL, kvm_set_kernel_irqchip,
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "kernel-irqchip",
|
||||
"Configure KVM in-kernel irqchip");
|
||||
|
||||
object_class_property_add(oc, "kvm-shadow-mem", "int",
|
||||
kvm_get_kvm_shadow_mem, kvm_set_kvm_shadow_mem,
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "kvm-shadow-mem",
|
||||
"KVM shadow MMU size");
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_accel_type = {
|
||||
.name = TYPE_KVM_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.instance_init = kvm_accel_instance_init,
|
||||
.class_init = kvm_accel_class_init,
|
||||
.instance_size = sizeof(KVMState),
|
||||
};
|
||||
|
||||
@@ -16,5 +16,4 @@ kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_
|
||||
kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
|
||||
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
|
||||
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32
|
||||
kvm_resample_fd_notify(int gsi) "gsi %d"
|
||||
|
||||
@@ -3,4 +3,3 @@ obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
|
||||
obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
|
||||
obj-$(call lnot,$(CONFIG_XEN)) += xen-stub.o
|
||||
@@ -138,18 +138,6 @@ void kvm_irqchip_commit_routes(KVMState *s)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_irqchip_add_change_notifier(Notifier *n)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_irqchip_remove_change_notifier(Notifier *n)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_irqchip_change_notify(void)
|
||||
{
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||
{
|
||||
return -ENOSYS;
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
void tb_flush(CPUState *cpu)
|
||||
@@ -22,10 +24,3 @@ void tb_flush(CPUState *cpu)
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
{
|
||||
}
|
||||
|
||||
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
||||
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
/* Handled by hardware accelerator. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
@@ -6,4 +6,3 @@ obj-y += translator.o
|
||||
|
||||
obj-$(CONFIG_USER_ONLY) += user-exec.o
|
||||
obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o
|
||||
obj-$(CONFIG_PLUGIN) += plugin-gen.o
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Common Atomic Helper Functions
|
||||
*
|
||||
* This file should be included before the various instantiations of
|
||||
* the atomic_template.h helpers.
|
||||
*
|
||||
* Copyright (c) 2019 Linaro
|
||||
* Written by Alex Bennée <alex.bennee@linaro.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
static inline
|
||||
void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
|
||||
trace_guest_mem_before_exec(cpu, addr, info);
|
||||
trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST);
|
||||
}
|
||||
|
||||
static inline void
|
||||
atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info | TRACE_MEM_ST);
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
{
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info);
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
{
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info);
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_st_post(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
|
||||
}
|
||||
+49
-52
@@ -18,7 +18,6 @@
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/plugin.h"
|
||||
#include "trace/mem.h"
|
||||
|
||||
#if DATA_SIZE == 16
|
||||
@@ -60,14 +59,37 @@
|
||||
# define ABI_TYPE uint32_t
|
||||
#endif
|
||||
|
||||
#define ATOMIC_TRACE_RMW do { \
|
||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
|
||||
\
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, \
|
||||
info | TRACE_MEM_ST); \
|
||||
} while (0)
|
||||
|
||||
#define ATOMIC_TRACE_LD do { \
|
||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
|
||||
\
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
|
||||
} while (0)
|
||||
|
||||
# define ATOMIC_TRACE_ST do { \
|
||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
|
||||
\
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
|
||||
} while (0)
|
||||
|
||||
/* Define host-endian atomic operations. Note that END is used within
|
||||
the ATOMIC_NAME macro, and redefined below. */
|
||||
#if DATA_SIZE == 1
|
||||
# define END
|
||||
# define MEND _be /* either le or be would be fine */
|
||||
#elif defined(HOST_WORDS_BIGENDIAN)
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#else
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
@@ -76,17 +98,14 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||
#endif
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_rmw_post(env, addr, info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -96,13 +115,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_ld_pre(env, addr, info);
|
||||
ATOMIC_TRACE_LD;
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_ld_post(env, addr, info);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -111,13 +127,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_st_pre(env, addr, info);
|
||||
ATOMIC_TRACE_ST;
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_st_post(env, addr, info);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
@@ -127,29 +140,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ATOMIC_TRACE_RMW;
|
||||
ret = atomic_xchg__nocheck(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_rmw_post(env, addr, info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
ret = atomic_##X(haddr, val); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, info); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
@@ -178,9 +186,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
smp_mb(); \
|
||||
cmp = atomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
@@ -188,7 +195,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
|
||||
} while (cmp != old); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, info); \
|
||||
return RET; \
|
||||
}
|
||||
|
||||
@@ -206,6 +212,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
#endif /* DATA SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
|
||||
@@ -213,8 +220,10 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
within the ATOMIC_NAME macro. */
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#else
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
@@ -223,17 +232,14 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#endif
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_rmw_post(env, addr, info);
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
||||
@@ -243,13 +249,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_ld_pre(env, addr, info);
|
||||
ATOMIC_TRACE_LD;
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_ld_post(env, addr, info);
|
||||
return BSWAP(val);
|
||||
}
|
||||
|
||||
@@ -258,15 +261,11 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
val = BSWAP(val);
|
||||
atomic_trace_st_pre(env, addr, info);
|
||||
ATOMIC_TRACE_ST;
|
||||
val = BSWAP(val);
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_st_post(env, addr, info);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
@@ -276,29 +275,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
ABI_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ATOMIC_TRACE_RMW;
|
||||
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_rmw_post(env, addr, info);
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
ret = atomic_##X(haddr, BSWAP(val)); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, info); \
|
||||
return BSWAP(ret); \
|
||||
}
|
||||
|
||||
@@ -325,9 +319,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE ldo, ldn, old, new, val = xval; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
smp_mb(); \
|
||||
ldn = atomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
@@ -335,7 +328,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
|
||||
} while (ldo != ldn); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, info); \
|
||||
return RET; \
|
||||
}
|
||||
|
||||
@@ -360,8 +352,13 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#undef ATOMIC_TRACE_ST
|
||||
#undef ATOMIC_TRACE_LD
|
||||
#undef ATOMIC_TRACE_RMW
|
||||
|
||||
#undef BSWAP
|
||||
#undef ABI_TYPE
|
||||
#undef DATA_TYPE
|
||||
|
||||
+18
-35
@@ -23,7 +23,7 @@
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
@@ -156,7 +156,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
||||
#if defined(DEBUG_DISAS)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
|
||||
&& qemu_log_in_addr_range(itb->pc)) {
|
||||
FILE *logfile = qemu_log_lock();
|
||||
qemu_log_lock();
|
||||
int flags = 0;
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
||||
flags |= CPU_DUMP_FPU;
|
||||
@@ -165,10 +165,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
||||
flags |= CPU_DUMP_CCOP;
|
||||
#endif
|
||||
log_cpu_state(cpu, flags);
|
||||
qemu_log_unlock(logfile);
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
|
||||
cpu->can_do_io = !use_icount;
|
||||
ret = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
cpu->can_do_io = 1;
|
||||
last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK);
|
||||
@@ -238,10 +239,10 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
uint32_t flags;
|
||||
uint32_t cflags = 1;
|
||||
uint32_t cf_mask = cflags & CF_HASH_MASK;
|
||||
/* volatile because we modify it between setjmp and longjmp */
|
||||
volatile bool in_exclusive_region = false;
|
||||
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
start_exclusive();
|
||||
|
||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
@@ -249,8 +250,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
mmap_unlock();
|
||||
}
|
||||
|
||||
start_exclusive();
|
||||
|
||||
/* Since we got here, we know that parallel_cpus must be true. */
|
||||
parallel_cpus = false;
|
||||
in_exclusive_region = true;
|
||||
cc->cpu_exec_enter(cpu);
|
||||
/* execute the generated code */
|
||||
trace_exec_tb(tb, pc);
|
||||
@@ -268,18 +272,16 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
assert_no_pages_locked();
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* As we start the exclusive region before codegen we must still
|
||||
* be in the region if we longjump out of either the codegen or
|
||||
* the execution.
|
||||
*/
|
||||
g_assert(cpu_in_exclusive_context(cpu));
|
||||
parallel_cpus = true;
|
||||
end_exclusive();
|
||||
if (in_exclusive_region) {
|
||||
/* We might longjump out of either the codegen or the
|
||||
* execution, so must make sure we only end the exclusive
|
||||
* region if we started it.
|
||||
*/
|
||||
parallel_cpus = true;
|
||||
end_exclusive();
|
||||
}
|
||||
}
|
||||
|
||||
struct tb_desc {
|
||||
@@ -504,17 +506,6 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
||||
cc->do_interrupt(cpu);
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu->exception_index = -1;
|
||||
|
||||
if (unlikely(cpu->singlestep_enabled)) {
|
||||
/*
|
||||
* After processing the exception, ensure an EXCP_DEBUG is
|
||||
* raised when single-stepping so that GDB doesn't miss the
|
||||
* next instruction.
|
||||
*/
|
||||
*ret = EXCP_DEBUG;
|
||||
cpu_handle_debug_exception(cpu);
|
||||
return true;
|
||||
}
|
||||
} else if (!replay_has_interrupt()) {
|
||||
/* give a chance to iothread in replay mode */
|
||||
*ret = EXCP_INTERRUPT;
|
||||
@@ -588,13 +579,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
else {
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
replay_interrupt();
|
||||
/*
|
||||
* After processing the interrupt, ensure an EXCP_DEBUG is
|
||||
* raised when single-stepping so that GDB doesn't miss the
|
||||
* next instruction.
|
||||
*/
|
||||
cpu->exception_index =
|
||||
(cpu->singlestep_enabled ? EXCP_DEBUG : -1);
|
||||
cpu->exception_index = -1;
|
||||
*last_tb = NULL;
|
||||
}
|
||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
||||
@@ -720,8 +705,6 @@ int cpu_exec(CPUState *cpu)
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
|
||||
assert_no_pages_locked();
|
||||
}
|
||||
|
||||
|
||||
+416
-1049
File diff suppressed because it is too large.
Load diff
@@ -1,932 +0,0 @@
|
||||
/*
|
||||
* plugin-gen.c - TCG-related bits of plugin infrastructure
|
||||
*
|
||||
* Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
|
||||
* License: GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* We support instrumentation at an instruction granularity. That is,
|
||||
* if a plugin wants to instrument the memory accesses performed by a
|
||||
* particular instruction, it can just do that instead of instrumenting
|
||||
* all memory accesses. Thus, in order to do this we first have to
|
||||
* translate a TB, so that plugins can decide what/where to instrument.
|
||||
*
|
||||
* Injecting the desired instrumentation could be done with a second
|
||||
* translation pass that combined the instrumentation requests, but that
|
||||
* would be ugly and inefficient since we would decode the guest code twice.
|
||||
* Instead, during TB translation we add "empty" instrumentation calls for all
|
||||
* possible instrumentation events, and then once we collect the instrumentation
|
||||
* requests from plugins, we either "fill in" those empty events or remove them
|
||||
* if they have no requests.
|
||||
*
|
||||
* When "filling in" an event we first copy the empty callback's TCG ops. This
|
||||
* might seem unnecessary, but it is done to support an arbitrary number
|
||||
* of callbacks per event. Take for example a regular instruction callback.
|
||||
* We first generate a callback to an empty helper function. Then, if two
|
||||
* plugins register one callback each for this instruction, we make two copies
|
||||
* of the TCG ops generated for the empty callback, substituting the function
|
||||
* pointer that points to the empty helper function with the plugins' desired
|
||||
* callback functions. After that we remove the empty callback's ops.
|
||||
*
|
||||
* Note that the location in TCGOp.args[] of the pointer to a helper function
|
||||
* varies across different guest and host architectures. Instead of duplicating
|
||||
* the logic that figures this out, we rely on the fact that the empty
|
||||
* callbacks point to empty functions that are unique pointers in the program.
|
||||
* Thus, to find the right location we just have to look for a match in
|
||||
* TCGOp.args[]. This is the main reason why we first copy an empty callback's
|
||||
* TCG ops and then fill them in; regardless of whether we have one or many
|
||||
* callbacks for that event, the logic to add all of them is the same.
|
||||
*
|
||||
* When generating more than one callback per event, we make a small
|
||||
* optimization to avoid generating redundant operations. For instance, for the
|
||||
* second and all subsequent callbacks of an event, we do not need to reload the
|
||||
* 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-op.h"
|
||||
#include "trace/mem.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
#include "exec/translator.h"
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
# define CONFIG_SOFTMMU_GATE 1
|
||||
#else
|
||||
# define CONFIG_SOFTMMU_GATE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* plugin_cb_start TCG op args[]:
|
||||
* 0: enum plugin_gen_from
|
||||
* 1: enum plugin_gen_cb
|
||||
* 2: set to 1 for mem callback that is a write, 0 otherwise.
|
||||
*/
|
||||
|
||||
enum plugin_gen_from {
|
||||
PLUGIN_GEN_FROM_TB,
|
||||
PLUGIN_GEN_FROM_INSN,
|
||||
PLUGIN_GEN_FROM_MEM,
|
||||
PLUGIN_GEN_AFTER_INSN,
|
||||
PLUGIN_GEN_N_FROMS,
|
||||
};
|
||||
|
||||
enum plugin_gen_cb {
|
||||
PLUGIN_GEN_CB_UDATA,
|
||||
PLUGIN_GEN_CB_INLINE,
|
||||
PLUGIN_GEN_CB_MEM,
|
||||
PLUGIN_GEN_ENABLE_MEM_HELPER,
|
||||
PLUGIN_GEN_DISABLE_MEM_HELPER,
|
||||
PLUGIN_GEN_N_CBS,
|
||||
};
|
||||
|
||||
/*
|
||||
* These helpers are stubs that get dynamically switched out for calls
|
||||
* direct to the plugin if they are subscribed to.
|
||||
*/
|
||||
void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata)
|
||||
{ }
|
||||
|
||||
void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
|
||||
qemu_plugin_meminfo_t info, uint64_t vaddr,
|
||||
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_new_i32();
|
||||
TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */
|
||||
|
||||
tcg_gen_ld_i32(cpu_index, cpu_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
|
||||
|
||||
tcg_temp_free_ptr(udata);
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
}
|
||||
|
||||
/*
|
||||
* For now we only support addi_i64.
|
||||
* When we support more ops, we can generate one empty inline cb for each.
|
||||
*/
|
||||
static void gen_empty_inline_cb(void)
|
||||
{
|
||||
TCGv_i64 val = tcg_temp_new_i64();
|
||||
TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */
|
||||
|
||||
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);
|
||||
tcg_gen_st_i64(val, ptr, 0);
|
||||
tcg_temp_free_ptr(ptr);
|
||||
tcg_temp_free_i64(val);
|
||||
}
|
||||
|
||||
static void gen_empty_mem_cb(TCGv addr, uint32_t info)
|
||||
{
|
||||
do_gen_mem_cb(addr, info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Share the same function for enable/disable. When enabling, the NULL
|
||||
* pointer will be overwritten later.
|
||||
*/
|
||||
static void gen_empty_mem_helper(void)
|
||||
{
|
||||
TCGv_ptr ptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static inline
|
||||
void gen_plugin_cb_start(enum plugin_gen_from from,
|
||||
enum plugin_gen_cb type, unsigned wr)
|
||||
{
|
||||
TCGOp *op;
|
||||
|
||||
tcg_gen_plugin_cb_start(from, type, wr);
|
||||
op = tcg_last_op();
|
||||
QSIMPLEQ_INSERT_TAIL(&tcg_ctx->plugin_ops, op, plugin_link);
|
||||
}
|
||||
|
||||
static void gen_wrapped(enum plugin_gen_from from,
|
||||
enum plugin_gen_cb type, void (*func)(void))
|
||||
{
|
||||
gen_plugin_cb_start(from, type, 0);
|
||||
func();
|
||||
tcg_gen_plugin_cb_end();
|
||||
}
|
||||
|
||||
static inline void plugin_gen_empty_callback(enum plugin_gen_from from)
|
||||
{
|
||||
switch (from) {
|
||||
case PLUGIN_GEN_AFTER_INSN:
|
||||
gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
|
||||
gen_empty_mem_helper);
|
||||
break;
|
||||
case PLUGIN_GEN_FROM_INSN:
|
||||
/*
|
||||
* Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being
|
||||
* the first callback of an instruction
|
||||
*/
|
||||
gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER,
|
||||
gen_empty_mem_helper);
|
||||
/* fall through */
|
||||
case PLUGIN_GEN_FROM_TB:
|
||||
gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb);
|
||||
gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int wr = !!(info & TRACE_MEM_ST);
|
||||
|
||||
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, wr);
|
||||
if (is_mem) {
|
||||
f->mem_fn(addr, info);
|
||||
} else {
|
||||
f->inline_fn();
|
||||
}
|
||||
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)
|
||||
{
|
||||
while (op) {
|
||||
if (op->opc == opc) {
|
||||
return op;
|
||||
}
|
||||
op = QTAILQ_NEXT(op, link);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end)
|
||||
{
|
||||
TCGOp *ret = QTAILQ_NEXT(end, link);
|
||||
|
||||
QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* remove all ops until (and including) plugin_cb_end */
|
||||
static TCGOp *rm_ops(TCGOp *op)
|
||||
{
|
||||
TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end);
|
||||
|
||||
tcg_debug_assert(end_op);
|
||||
return rm_ops_range(op, end_op);
|
||||
}
|
||||
|
||||
static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
*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;
|
||||
}
|
||||
|
||||
static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc)
|
||||
{
|
||||
op = copy_op_nocheck(begin_op, op);
|
||||
tcg_debug_assert((*begin_op)->opc == 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);
|
||||
/* movi_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_movi_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_movi_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* 2x movi_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_movi_i32);
|
||||
op->args[1] = v;
|
||||
|
||||
op = copy_op(begin_op, op, INDEX_op_movi_i32);
|
||||
op->args[1] = v >> 32;
|
||||
} else {
|
||||
/* movi_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_movi_i64);
|
||||
op->args[1] = v;
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
/* movi_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_movi_i32);
|
||||
op->args[1] = (uintptr_t)ptr;
|
||||
} else {
|
||||
/* movi_i64 */
|
||||
op = copy_movi_i64(begin_op, op, (uint64_t)(uintptr_t)ptr);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_const_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
|
||||
{
|
||||
return copy_movi_i64(begin_op, op, v);
|
||||
}
|
||||
|
||||
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) {
|
||||
/* 2x ld_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
} else {
|
||||
/* ld_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* 2x st_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_st_i32);
|
||||
op = copy_op(begin_op, op, INDEX_op_st_i32);
|
||||
} else {
|
||||
/* st_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_st_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* all 32-bit backends must implement add2_i32 */
|
||||
g_assert(TCG_TARGET_HAS_add2_i32);
|
||||
op = copy_op(begin_op, op, INDEX_op_add2_i32);
|
||||
} else {
|
||||
op = copy_op(begin_op, op, INDEX_op_add_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
/* st_i32 */
|
||||
op = copy_op(begin_op, op, INDEX_op_st_i32);
|
||||
} else {
|
||||
/* st_i64 */
|
||||
op = copy_st_i64(begin_op, op);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
|
||||
void *func, unsigned tcg_flags, int *cb_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 */
|
||||
op->param1 = (*begin_op)->param1;
|
||||
op->param2 = (*begin_op)->param2;
|
||||
tcg_debug_assert(op->life == 0);
|
||||
if (*cb_idx == -1) {
|
||||
int i;
|
||||
|
||||
/*
|
||||
* 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] = tcg_flags;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
TCGOp *begin_op, TCGOp *op, int *cb_idx)
|
||||
{
|
||||
/* const_ptr */
|
||||
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 = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
|
||||
memcpy(op->args, begin_op->args, sizeof(op->args));
|
||||
}
|
||||
|
||||
/* call */
|
||||
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb),
|
||||
cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
TCGOp *begin_op, TCGOp *op,
|
||||
int *unused)
|
||||
{
|
||||
/* const_ptr */
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
|
||||
/* ld_i64 */
|
||||
op = copy_ld_i64(&begin_op, op);
|
||||
|
||||
/* const_i64 */
|
||||
op = copy_const_i64(&begin_op, op, cb->inline_insn.imm);
|
||||
|
||||
/* add_i64 */
|
||||
op = copy_add_i64(&begin_op, op);
|
||||
|
||||
/* st_i64 */
|
||||
op = copy_st_i64(&begin_op, op);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
TCGOp *begin_op, TCGOp *op, int *cb_idx)
|
||||
{
|
||||
enum plugin_gen_cb type = begin_op->args[1];
|
||||
|
||||
tcg_debug_assert(type == PLUGIN_GEN_CB_MEM);
|
||||
|
||||
/* const_i32 == movi_i32 ("info", so it remains as is) */
|
||||
op = copy_op(&begin_op, op, INDEX_op_movi_i32);
|
||||
|
||||
/* const_ptr */
|
||||
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 = 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),
|
||||
cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb,
|
||||
TCGOp *begin_op, TCGOp *op, int *intp);
|
||||
typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb);
|
||||
|
||||
static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
|
||||
{
|
||||
int w;
|
||||
|
||||
w = op->args[2];
|
||||
return !!(cb->rw & (w + 1));
|
||||
}
|
||||
|
||||
static inline
|
||||
void inject_cb_type(const GArray *cbs, TCGOp *begin_op, inject_fn inject,
|
||||
op_ok_fn ok)
|
||||
{
|
||||
TCGOp *end_op;
|
||||
TCGOp *op;
|
||||
int cb_idx = -1;
|
||||
int i;
|
||||
|
||||
if (!cbs || cbs->len == 0) {
|
||||
rm_ops(begin_op);
|
||||
return;
|
||||
}
|
||||
|
||||
end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
|
||||
tcg_debug_assert(end_op);
|
||||
|
||||
op = end_op;
|
||||
for (i = 0; i < cbs->len; i++) {
|
||||
struct qemu_plugin_dyn_cb *cb =
|
||||
&g_array_index(cbs, struct qemu_plugin_dyn_cb, i);
|
||||
|
||||
if (!ok(begin_op, cb)) {
|
||||
continue;
|
||||
}
|
||||
op = inject(cb, begin_op, op, &cb_idx);
|
||||
}
|
||||
rm_ops_range(begin_op, end_op);
|
||||
}
|
||||
|
||||
static void
|
||||
inject_udata_cb(const GArray *cbs, TCGOp *begin_op)
|
||||
{
|
||||
inject_cb_type(cbs, begin_op, append_udata_cb, op_ok);
|
||||
}
|
||||
|
||||
static void
|
||||
inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok)
|
||||
{
|
||||
inject_cb_type(cbs, begin_op, append_inline_cb, ok);
|
||||
}
|
||||
|
||||
static void
|
||||
inject_mem_cb(const GArray *cbs, TCGOp *begin_op)
|
||||
{
|
||||
inject_cb_type(cbs, begin_op, append_mem_cb, op_rw);
|
||||
}
|
||||
|
||||
/* we could change the ops in place, but we can reuse more code by copying */
|
||||
static void inject_mem_helper(TCGOp *begin_op, GArray *arr)
|
||||
{
|
||||
TCGOp *orig_op = begin_op;
|
||||
TCGOp *end_op;
|
||||
TCGOp *op;
|
||||
|
||||
end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
|
||||
tcg_debug_assert(end_op);
|
||||
|
||||
/* const ptr */
|
||||
op = copy_const_ptr(&begin_op, end_op, arr);
|
||||
|
||||
/* st_ptr */
|
||||
op = copy_st_ptr(&begin_op, op);
|
||||
|
||||
rm_ops_range(orig_op, end_op);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tracking memory accesses performed from helpers requires extra work.
|
||||
* If an instruction is emulated with helpers, we do two things:
|
||||
* (1) copy the CB descriptors, and keep track of it so that they can be
|
||||
* freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so
|
||||
* that we can read them at run-time (i.e. when the helper executes).
|
||||
* This run-time access is performed from qemu_plugin_vcpu_mem_cb.
|
||||
*
|
||||
* Note that plugin_gen_disable_mem_helpers undoes (2). Since it
|
||||
* 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_insn *plugin_insn,
|
||||
TCGOp *begin_op)
|
||||
{
|
||||
GArray *cbs[2];
|
||||
GArray *arr;
|
||||
size_t n_cbs, i;
|
||||
|
||||
cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR];
|
||||
cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
|
||||
|
||||
n_cbs = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(cbs); i++) {
|
||||
n_cbs += cbs[i]->len;
|
||||
}
|
||||
|
||||
plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs;
|
||||
if (likely(!plugin_insn->mem_helper)) {
|
||||
rm_ops(begin_op);
|
||||
return;
|
||||
}
|
||||
|
||||
arr = g_array_sized_new(false, false,
|
||||
sizeof(struct qemu_plugin_dyn_cb), n_cbs);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cbs); i++) {
|
||||
g_array_append_vals(arr, cbs[i]->data, cbs[i]->len);
|
||||
}
|
||||
|
||||
qemu_plugin_add_dyn_cb_arr(arr);
|
||||
inject_mem_helper(begin_op, arr);
|
||||
}
|
||||
|
||||
static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn,
|
||||
TCGOp *begin_op)
|
||||
{
|
||||
if (likely(!plugin_insn->mem_helper)) {
|
||||
rm_ops(begin_op);
|
||||
return;
|
||||
}
|
||||
inject_mem_helper(begin_op, NULL);
|
||||
}
|
||||
|
||||
/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
|
||||
void plugin_gen_disable_mem_helpers(void)
|
||||
{
|
||||
TCGv_ptr ptr;
|
||||
|
||||
if (likely(tcg_ctx->plugin_insn == NULL ||
|
||||
!tcg_ctx->plugin_insn->mem_helper)) {
|
||||
return;
|
||||
}
|
||||
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,
|
||||
TCGOp *begin_op)
|
||||
{
|
||||
inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op);
|
||||
}
|
||||
|
||||
static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb,
|
||||
TCGOp *begin_op)
|
||||
{
|
||||
inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok);
|
||||
}
|
||||
|
||||
static void plugin_gen_insn_udata(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_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op);
|
||||
}
|
||||
|
||||
static void plugin_gen_insn_inline(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_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
|
||||
begin_op, op_ok);
|
||||
}
|
||||
|
||||
static void plugin_gen_mem_regular(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_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op);
|
||||
}
|
||||
|
||||
static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb,
|
||||
TCGOp *begin_op, int insn_idx)
|
||||
{
|
||||
const GArray *cbs;
|
||||
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
|
||||
|
||||
cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
|
||||
inject_inline_cb(cbs, begin_op, op_rw);
|
||||
}
|
||||
|
||||
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(insn, begin_op);
|
||||
}
|
||||
|
||||
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);
|
||||
inject_mem_disable_helper(insn, begin_op);
|
||||
}
|
||||
|
||||
static void plugin_inject_cb(const struct qemu_plugin_tb *ptb, TCGOp *begin_op,
|
||||
int insn_idx)
|
||||
{
|
||||
enum plugin_gen_from from = begin_op->args[0];
|
||||
enum plugin_gen_cb type = begin_op->args[1];
|
||||
|
||||
switch (from) {
|
||||
case PLUGIN_GEN_FROM_TB:
|
||||
switch (type) {
|
||||
case PLUGIN_GEN_CB_UDATA:
|
||||
plugin_gen_tb_udata(ptb, begin_op);
|
||||
return;
|
||||
case PLUGIN_GEN_CB_INLINE:
|
||||
plugin_gen_tb_inline(ptb, begin_op);
|
||||
return;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
case PLUGIN_GEN_FROM_INSN:
|
||||
switch (type) {
|
||||
case PLUGIN_GEN_CB_UDATA:
|
||||
plugin_gen_insn_udata(ptb, begin_op, insn_idx);
|
||||
return;
|
||||
case PLUGIN_GEN_CB_INLINE:
|
||||
plugin_gen_insn_inline(ptb, begin_op, insn_idx);
|
||||
return;
|
||||
case PLUGIN_GEN_ENABLE_MEM_HELPER:
|
||||
plugin_gen_enable_mem_helper(ptb, begin_op, insn_idx);
|
||||
return;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
case PLUGIN_GEN_FROM_MEM:
|
||||
switch (type) {
|
||||
case PLUGIN_GEN_CB_MEM:
|
||||
plugin_gen_mem_regular(ptb, begin_op, insn_idx);
|
||||
return;
|
||||
case PLUGIN_GEN_CB_INLINE:
|
||||
plugin_gen_mem_inline(ptb, begin_op, insn_idx);
|
||||
return;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
case PLUGIN_GEN_AFTER_INSN:
|
||||
switch (type) {
|
||||
case PLUGIN_GEN_DISABLE_MEM_HELPER:
|
||||
plugin_gen_disable_mem_helper(ptb, begin_op, insn_idx);
|
||||
return;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
/* #define DEBUG_PLUGIN_GEN_OPS */
|
||||
static void pr_ops(void)
|
||||
{
|
||||
#ifdef DEBUG_PLUGIN_GEN_OPS
|
||||
TCGOp *op;
|
||||
int i = 0;
|
||||
|
||||
QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
|
||||
const char *name = "";
|
||||
const char *type = "";
|
||||
|
||||
if (op->opc == INDEX_op_plugin_cb_start) {
|
||||
switch (op->args[0]) {
|
||||
case PLUGIN_GEN_FROM_TB:
|
||||
name = "tb";
|
||||
break;
|
||||
case PLUGIN_GEN_FROM_INSN:
|
||||
name = "insn";
|
||||
break;
|
||||
case PLUGIN_GEN_FROM_MEM:
|
||||
name = "mem";
|
||||
break;
|
||||
case PLUGIN_GEN_AFTER_INSN:
|
||||
name = "after insn";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (op->args[1]) {
|
||||
case PLUGIN_GEN_CB_UDATA:
|
||||
type = "udata";
|
||||
break;
|
||||
case PLUGIN_GEN_CB_INLINE:
|
||||
type = "inline";
|
||||
break;
|
||||
case PLUGIN_GEN_CB_MEM:
|
||||
type = "mem";
|
||||
break;
|
||||
case PLUGIN_GEN_ENABLE_MEM_HELPER:
|
||||
type = "enable mem helper";
|
||||
break;
|
||||
case PLUGIN_GEN_DISABLE_MEM_HELPER:
|
||||
type = "disable mem helper";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type);
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
|
||||
{
|
||||
TCGOp *op;
|
||||
int insn_idx;
|
||||
|
||||
pr_ops();
|
||||
insn_idx = -1;
|
||||
QSIMPLEQ_FOREACH(op, &tcg_ctx->plugin_ops, plugin_link) {
|
||||
enum plugin_gen_from from = op->args[0];
|
||||
enum plugin_gen_cb type = op->args[1];
|
||||
|
||||
tcg_debug_assert(op->opc == INDEX_op_plugin_cb_start);
|
||||
/* ENABLE_MEM_HELPER is the first callback of an instruction */
|
||||
if (from == PLUGIN_GEN_FROM_INSN &&
|
||||
type == PLUGIN_GEN_ENABLE_MEM_HELPER) {
|
||||
insn_idx++;
|
||||
}
|
||||
plugin_inject_cb(plugin_tb, op, insn_idx);
|
||||
}
|
||||
pr_ops();
|
||||
}
|
||||
|
||||
bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb)
|
||||
{
|
||||
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
|
||||
bool ret = false;
|
||||
|
||||
if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) {
|
||||
ret = true;
|
||||
|
||||
QSIMPLEQ_INIT(&tcg_ctx->plugin_ops);
|
||||
ptb->vaddr = tb->pc;
|
||||
ptb->vaddr2 = -1;
|
||||
get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1);
|
||||
ptb->haddr2 = NULL;
|
||||
|
||||
plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
|
||||
{
|
||||
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
|
||||
struct qemu_plugin_insn *pinsn;
|
||||
|
||||
pinsn = qemu_plugin_tb_insn_get(ptb);
|
||||
tcg_ctx->plugin_insn = pinsn;
|
||||
pinsn->vaddr = db->pc_next;
|
||||
plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
|
||||
|
||||
/*
|
||||
* Detect page crossing to get the new host address.
|
||||
* Note that we skip this when haddr1 == NULL, e.g. when we're
|
||||
* fetching instructions from a region not backed by RAM.
|
||||
*/
|
||||
if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) &&
|
||||
unlikely((db->pc_next & TARGET_PAGE_MASK) !=
|
||||
(db->pc_first & TARGET_PAGE_MASK))) {
|
||||
get_page_addr_code_hostp(cpu->env_ptr, db->pc_next,
|
||||
&ptb->haddr2);
|
||||
ptb->vaddr2 = db->pc_next;
|
||||
}
|
||||
if (likely(ptb->vaddr2 == -1)) {
|
||||
pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
|
||||
} else {
|
||||
pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
|
||||
}
|
||||
}
|
||||
|
||||
void plugin_gen_insn_end(void)
|
||||
{
|
||||
plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
|
||||
}
|
||||
|
||||
void plugin_gen_tb_end(CPUState *cpu)
|
||||
{
|
||||
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
|
||||
int i;
|
||||
|
||||
/* collect instrumentation requests */
|
||||
qemu_plugin_tb_trans_cb(cpu, ptb);
|
||||
|
||||
/* inject the instrumentation at the appropriate places */
|
||||
plugin_gen_inject(ptb);
|
||||
|
||||
/* clean up */
|
||||
for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
|
||||
if (ptb->cbs[i]) {
|
||||
g_array_set_size(ptb->cbs[i], 0);
|
||||
}
|
||||
}
|
||||
ptb->n = 0;
|
||||
tcg_ctx->plugin_insn = NULL;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#ifdef CONFIG_PLUGIN
|
||||
/* Note: no TCG flags because those are overwritten later */
|
||||
DEF_HELPER_2(plugin_vcpu_udata_cb, void, i32, ptr)
|
||||
DEF_HELPER_4(plugin_vcpu_mem_cb, void, i32, i32, i64, ptr)
|
||||
#endif
|
||||
+5
-141
@@ -25,28 +25,14 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "qom/object.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
|
||||
typedef struct TCGState {
|
||||
AccelState parent_obj;
|
||||
|
||||
bool mttcg_enabled;
|
||||
unsigned long tb_size;
|
||||
} TCGState;
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
|
||||
#define TCG_STATE(obj) \
|
||||
OBJECT_CHECK(TCGState, (obj), TYPE_TCG_ACCEL)
|
||||
unsigned long tcg_tb_size;
|
||||
|
||||
/* mask must never be zero, except for A20 change call */
|
||||
static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
@@ -73,149 +59,27 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We default to false if we know other options have been enabled
|
||||
* which are currently incompatible with MTTCG. Otherwise when each
|
||||
* guest (target) has been updated to support:
|
||||
* - atomic instructions
|
||||
* - memory ordering primitives (barriers)
|
||||
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
|
||||
*
|
||||
* Once a guest architecture has been converted to the new primitives
|
||||
* 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 (use_icount || TCG_OVERSIZED_GUEST) {
|
||||
return false;
|
||||
} else {
|
||||
#ifdef TARGET_SUPPORTS_MTTCG
|
||||
return check_tcg_memory_orders_compatible();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_accel_instance_init(Object *obj)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
s->mttcg_enabled = default_mttcg_enabled();
|
||||
}
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
|
||||
tcg_exec_init(s->tb_size * 1024 * 1024);
|
||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||
cpu_interrupt_handler = tcg_handle_interrupt;
|
||||
mttcg_enabled = s->mttcg_enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *tcg_get_thread(Object *obj, Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
return g_strdup(s->mttcg_enabled ? "multi" : "single");
|
||||
}
|
||||
|
||||
static void tcg_set_thread(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
if (strcmp(value, "multi") == 0) {
|
||||
if (TCG_OVERSIZED_GUEST) {
|
||||
error_setg(errp, "No MTTCG when guest word size > hosts");
|
||||
} else if (use_icount) {
|
||||
error_setg(errp, "No MTTCG when icount is enabled");
|
||||
} else {
|
||||
#ifndef TARGET_SUPPORTS_MTTCG
|
||||
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) {
|
||||
s->mttcg_enabled = false;
|
||||
} else {
|
||||
error_setg(errp, "Invalid 'thread' setting %s", value);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_get_tb_size(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
uint32_t value = s->tb_size;
|
||||
|
||||
visit_type_uint32(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void tcg_set_tb_size(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
uint32_t value;
|
||||
|
||||
if (!visit_type_uint32(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->tb_size = value;
|
||||
}
|
||||
|
||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->allowed = &tcg_allowed;
|
||||
|
||||
object_class_property_add_str(oc, "thread",
|
||||
tcg_get_thread,
|
||||
tcg_set_thread);
|
||||
|
||||
object_class_property_add(oc, "tb-size", "int",
|
||||
tcg_get_tb_size, tcg_set_tb_size,
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "tb-size",
|
||||
"TCG translation block cache size");
|
||||
|
||||
}
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
|
||||
static const TypeInfo tcg_accel_type = {
|
||||
.name = TYPE_TCG_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.instance_init = tcg_accel_instance_init,
|
||||
.class_init = tcg_accel_class_init,
|
||||
.instance_size = sizeof(TCGState),
|
||||
};
|
||||
|
||||
static void register_accel_types(void)
|
||||
|
||||
+182
-260
@@ -21,9 +21,51 @@
|
||||
#include "qemu/host-utils.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "tcg/tcg-gvec-desc.h"
|
||||
#include "tcg-gvec-desc.h"
|
||||
|
||||
|
||||
/* Virtually all hosts support 16-byte vectors. Those that don't can emulate
|
||||
* them via GCC's generic vector extension. This turns out to be simpler and
|
||||
* more reliable than getting the compiler to autovectorize.
|
||||
*
|
||||
* In tcg-op-gvec.c, we asserted that both the size and alignment of the data
|
||||
* are multiples of 16.
|
||||
*
|
||||
* When the compiler does not support all of the operations we require, the
|
||||
* loops are written so that we can always fall back on the base types.
|
||||
*/
|
||||
#ifdef CONFIG_VECTOR16
|
||||
typedef uint8_t vec8 __attribute__((vector_size(16)));
|
||||
typedef uint16_t vec16 __attribute__((vector_size(16)));
|
||||
typedef uint32_t vec32 __attribute__((vector_size(16)));
|
||||
typedef uint64_t vec64 __attribute__((vector_size(16)));
|
||||
|
||||
typedef int8_t svec8 __attribute__((vector_size(16)));
|
||||
typedef int16_t svec16 __attribute__((vector_size(16)));
|
||||
typedef int32_t svec32 __attribute__((vector_size(16)));
|
||||
typedef int64_t svec64 __attribute__((vector_size(16)));
|
||||
|
||||
#define DUP16(X) { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }
|
||||
#define DUP8(X) { X, X, X, X, X, X, X, X }
|
||||
#define DUP4(X) { X, X, X, X }
|
||||
#define DUP2(X) { X, X }
|
||||
#else
|
||||
typedef uint8_t vec8;
|
||||
typedef uint16_t vec16;
|
||||
typedef uint32_t vec32;
|
||||
typedef uint64_t vec64;
|
||||
|
||||
typedef int8_t svec8;
|
||||
typedef int16_t svec16;
|
||||
typedef int32_t svec32;
|
||||
typedef int64_t svec64;
|
||||
|
||||
#define DUP16(X) X
|
||||
#define DUP8(X) X
|
||||
#define DUP4(X) X
|
||||
#define DUP2(X) X
|
||||
#endif /* CONFIG_VECTOR16 */
|
||||
|
||||
static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc)
|
||||
{
|
||||
intptr_t maxsz = simd_maxsz(desc);
|
||||
@@ -41,8 +83,8 @@ void HELPER(gvec_add8)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) + *(uint8_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -52,8 +94,8 @@ void HELPER(gvec_add16)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) + *(uint16_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -63,8 +105,8 @@ void HELPER(gvec_add32)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) + *(uint32_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -74,8 +116,8 @@ void HELPER(gvec_add64)(void *d, void *a, void *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) + *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) + *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -83,10 +125,11 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc)
|
||||
void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec8 vecb = (vec8)DUP16(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) + (uint8_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -94,10 +137,11 @@ void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec16 vecb = (vec16)DUP8(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) + (uint16_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -105,10 +149,11 @@ void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec32 vecb = (vec32)DUP4(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) + (uint32_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -116,10 +161,11 @@ void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) + b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -129,8 +175,8 @@ void HELPER(gvec_sub8)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) - *(uint8_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -140,8 +186,8 @@ void HELPER(gvec_sub16)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) - *(uint16_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -151,8 +197,8 @@ void HELPER(gvec_sub32)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) - *(uint32_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -162,8 +208,8 @@ void HELPER(gvec_sub64)(void *d, void *a, void *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) - *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) - *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -171,10 +217,11 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc)
|
||||
void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec8 vecb = (vec8)DUP16(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) - (uint8_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -182,10 +229,11 @@ void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec16 vecb = (vec16)DUP8(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) - (uint16_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -193,10 +241,11 @@ void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec32 vecb = (vec32)DUP4(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) - (uint32_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -204,10 +253,11 @@ void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) - b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -217,8 +267,8 @@ void HELPER(gvec_mul8)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) * *(uint8_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -228,8 +278,8 @@ void HELPER(gvec_mul16)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) * *(uint16_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -239,8 +289,8 @@ void HELPER(gvec_mul32)(void *d, void *a, void *b, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) * *(uint32_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -250,8 +300,8 @@ void HELPER(gvec_mul64)(void *d, void *a, void *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) * *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) * *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -259,10 +309,11 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc)
|
||||
void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec8 vecb = (vec8)DUP16(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) * (uint8_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -270,10 +321,11 @@ void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec16 vecb = (vec16)DUP8(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) * (uint16_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -281,10 +333,11 @@ void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec32 vecb = (vec32)DUP4(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) * (uint32_t)b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -292,10 +345,11 @@ void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) * b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -305,8 +359,8 @@ void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = -*(uint8_t *)(a + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = -*(vec8 *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -316,8 +370,8 @@ void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = -*(uint16_t *)(a + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = -*(vec16 *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -327,8 +381,8 @@ void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = -*(uint32_t *)(a + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = -*(vec32 *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -338,8 +392,8 @@ void HELPER(gvec_neg64)(void *d, void *a, 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);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = -*(vec64 *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -445,8 +499,8 @@ void HELPER(gvec_not)(void *d, void *a, 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);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~*(vec64 *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -456,8 +510,8 @@ void HELPER(gvec_and)(void *d, void *a, void *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) & *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) & *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -467,8 +521,8 @@ void HELPER(gvec_or)(void *d, void *a, void *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) | *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) | *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -478,8 +532,8 @@ void HELPER(gvec_xor)(void *d, void *a, void *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) ^ *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) ^ *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -489,8 +543,8 @@ void HELPER(gvec_andc)(void *d, void *a, void *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) &~ *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) &~ *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -500,8 +554,8 @@ void HELPER(gvec_orc)(void *d, void *a, void *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) |~ *(uint64_t *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) |~ *(vec64 *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -511,8 +565,8 @@ void HELPER(gvec_nand)(void *d, void *a, void *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) & *(uint64_t *)(b + i));
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -522,8 +576,8 @@ void HELPER(gvec_nor)(void *d, void *a, void *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) | *(uint64_t *)(b + i));
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -533,8 +587,8 @@ void HELPER(gvec_eqv)(void *d, void *a, void *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) ^ *(uint64_t *)(b + i));
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -542,10 +596,11 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
|
||||
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) & b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -553,10 +608,11 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -564,10 +620,11 @@ void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) | b;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -578,8 +635,8 @@ void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -590,8 +647,8 @@ void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -602,8 +659,8 @@ void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -614,8 +671,8 @@ void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -626,8 +683,8 @@ void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -638,8 +695,8 @@ void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -650,8 +707,8 @@ void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -662,8 +719,8 @@ void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -674,8 +731,8 @@ void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(int8_t *)(d + i) = *(int8_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -686,8 +743,8 @@ void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(int16_t *)(d + i) = *(int16_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -698,8 +755,8 @@ void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(int32_t *)(d + i) = *(int32_t *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -710,56 +767,8 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(int64_t *)(d + i) = *(int64_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl8i)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = rol8(*(uint8_t *)(a + i), shift);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl16i)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = rol16(*(uint16_t *)(a + i), shift);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl32i)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = rol32(*(uint32_t *)(a + i), shift);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl64i)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = rol64(*(uint64_t *)(a + i), shift);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -908,101 +917,13 @@ void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl8v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
uint8_t sh = *(uint8_t *)(b + i) & 7;
|
||||
*(uint8_t *)(d + i) = rol8(*(uint8_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl16v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(uint16_t *)(d + i) = rol16(*(uint16_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl32v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(uint32_t *)(d + i) = rol32(*(uint32_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotl64v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(uint64_t *)(d + i) = rol64(*(uint64_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotr8v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
uint8_t sh = *(uint8_t *)(b + i) & 7;
|
||||
*(uint8_t *)(d + i) = ror8(*(uint8_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotr16v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(uint16_t *)(d + i) = ror16(*(uint16_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotr32v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(uint32_t *)(d + i) = ror32(*(uint32_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_rotr64v)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(uint64_t *)(d + i) = ror64(*(uint64_t *)(a + i), sh);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
/* If vectors are enabled, the compiler fills in -1 for true.
|
||||
Otherwise, we must take care of this by hand. */
|
||||
#ifdef CONFIG_VECTOR16
|
||||
# define DO_CMP0(X) X
|
||||
#else
|
||||
# define DO_CMP0(X) -(X)
|
||||
#endif
|
||||
|
||||
#define DO_CMP1(NAME, TYPE, OP) \
|
||||
void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \
|
||||
@@ -1010,24 +931,25 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \
|
||||
intptr_t oprsz = simd_oprsz(desc); \
|
||||
intptr_t i; \
|
||||
for (i = 0; i < oprsz; i += sizeof(TYPE)) { \
|
||||
*(TYPE *)(d + i) = -(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||
} \
|
||||
clear_high(d, oprsz, desc); \
|
||||
}
|
||||
|
||||
#define DO_CMP2(SZ) \
|
||||
DO_CMP1(gvec_eq##SZ, uint##SZ##_t, ==) \
|
||||
DO_CMP1(gvec_ne##SZ, uint##SZ##_t, !=) \
|
||||
DO_CMP1(gvec_lt##SZ, int##SZ##_t, <) \
|
||||
DO_CMP1(gvec_le##SZ, int##SZ##_t, <=) \
|
||||
DO_CMP1(gvec_ltu##SZ, uint##SZ##_t, <) \
|
||||
DO_CMP1(gvec_leu##SZ, uint##SZ##_t, <=)
|
||||
DO_CMP1(gvec_eq##SZ, vec##SZ, ==) \
|
||||
DO_CMP1(gvec_ne##SZ, vec##SZ, !=) \
|
||||
DO_CMP1(gvec_lt##SZ, svec##SZ, <) \
|
||||
DO_CMP1(gvec_le##SZ, svec##SZ, <=) \
|
||||
DO_CMP1(gvec_ltu##SZ, vec##SZ, <) \
|
||||
DO_CMP1(gvec_leu##SZ, vec##SZ, <=)
|
||||
|
||||
DO_CMP2(8)
|
||||
DO_CMP2(16)
|
||||
DO_CMP2(32)
|
||||
DO_CMP2(64)
|
||||
|
||||
#undef DO_CMP0
|
||||
#undef DO_CMP1
|
||||
#undef DO_CMP2
|
||||
|
||||
@@ -1528,11 +1450,11 @@ void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc)
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint64_t aa = *(uint64_t *)(a + i);
|
||||
uint64_t bb = *(uint64_t *)(b + i);
|
||||
uint64_t cc = *(uint64_t *)(c + i);
|
||||
*(uint64_t *)(d + i) = (bb & aa) | (cc & ~aa);
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
vec64 aa = *(vec64 *)(a + i);
|
||||
vec64 bb = *(vec64 *)(b + i);
|
||||
vec64 cc = *(vec64 *)(c + i);
|
||||
*(vec64 *)(d + i) = (bb & aa) | (cc & ~aa);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "exec/tb-lookup.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/log.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
/* 32-bit helpers */
|
||||
|
||||
|
||||
@@ -259,11 +259,6 @@ DEF_HELPER_FLAGS_3(gvec_sar16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_rotl8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_rotl16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_rotl32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_rotl64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_shl8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
@@ -279,16 +274,6 @@ DEF_HELPER_FLAGS_4(gvec_sar16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_rotl8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_rotl16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_rotl32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_rotl64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_rotr8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_rotr16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_rotr32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_rotr64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# TCG related tracing
|
||||
# TCG related tracing (mostly disabled by default)
|
||||
# cpu-exec.c
|
||||
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"
|
||||
disable exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
disable exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
disable exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x"
|
||||
|
||||
# translate-all.c
|
||||
translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
||||
+123
-152
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define NO_CPU_IO_DEFS
|
||||
@@ -26,7 +25,7 @@
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
@@ -173,13 +172,8 @@ struct page_collection {
|
||||
#define TB_FOR_EACH_JMP(head_tb, tb, n) \
|
||||
TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next)
|
||||
|
||||
/*
|
||||
* In system mode we want L1_MAP to be based on ram offsets,
|
||||
* while in user mode we want it to be based on virtual addresses.
|
||||
*
|
||||
* TODO: For user mode, see the caveat re host vs guest virtual
|
||||
* address spaces near GUEST_ADDR_MAX.
|
||||
*/
|
||||
/* In system mode we want L1_MAP to be based on ram offsets,
|
||||
while in user mode we want it to be based on virtual addresses. */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS
|
||||
# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
|
||||
@@ -187,7 +181,7 @@ struct page_collection {
|
||||
# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS
|
||||
#endif
|
||||
#else
|
||||
# define L1_MAP_ADDR_SPACE_BITS MIN(HOST_LONG_BITS, TARGET_ABI_BITS)
|
||||
# define L1_MAP_ADDR_SPACE_BITS TARGET_VIRT_ADDR_SPACE_BITS
|
||||
#endif
|
||||
|
||||
/* Size of the L2 (and L3, etc) page tables. */
|
||||
@@ -384,11 +378,6 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tb_destroy(TranslationBlock *tb)
|
||||
{
|
||||
qemu_spin_destroy(&tb->jmp_lock);
|
||||
}
|
||||
|
||||
bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
@@ -418,7 +407,6 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
/* one-shot translation, invalidate it immediately */
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tcg_tb_remove(tb);
|
||||
tb_destroy(tb);
|
||||
}
|
||||
r = true;
|
||||
}
|
||||
@@ -547,15 +535,6 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
|
||||
#endif
|
||||
existing = atomic_cmpxchg(lp, NULL, pd);
|
||||
if (unlikely(existing)) {
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < V_L2_SIZE; i++) {
|
||||
qemu_spin_destroy(&pd[i].lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
g_free(pd);
|
||||
pd = existing;
|
||||
}
|
||||
@@ -912,61 +891,43 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
/* Currently it is not recommended to allocate big chunks of data in
|
||||
user mode. It will change when a dedicated libc will be used. */
|
||||
/* ??? 64-bit hosts ought to have no problem mmaping data outside the
|
||||
region in which the guest needs to run. Revisit this. */
|
||||
#define USE_STATIC_CODE_GEN_BUFFER
|
||||
#endif
|
||||
|
||||
/* Minimum size of the code gen buffer. This number is randomly chosen,
|
||||
but not so small that we can't have a fair number of TB's live. */
|
||||
#define MIN_CODE_GEN_BUFFER_SIZE (1 * MiB)
|
||||
#define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024)
|
||||
|
||||
/* Maximum size of the code gen buffer we'd like to use. Unless otherwise
|
||||
indicated, this is constrained by the range of direct branches on the
|
||||
host cpu, as used by the TCG implementation of goto_tb. */
|
||||
#if defined(__x86_64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
#elif defined(__sparc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
#elif defined(__powerpc64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
#elif defined(__powerpc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024)
|
||||
#elif defined(__aarch64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
#elif defined(__s390x__)
|
||||
/* We have a +- 4GB range on the branches; leave some slop. */
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024)
|
||||
#elif defined(__mips__)
|
||||
/* We have a 256MB branch region, but leave room to make sure the
|
||||
main executable is also within that region. */
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (128 * MiB)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024)
|
||||
#else
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
|
||||
#endif
|
||||
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32 * MiB)
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* For user mode on smaller 32 bit systems we may run into trouble
|
||||
* allocating big chunks of data in the right place. On these systems
|
||||
* we utilise a static code generation buffer directly in the binary.
|
||||
*/
|
||||
#define USE_STATIC_CODE_GEN_BUFFER
|
||||
#endif
|
||||
#else /* TCG_TARGET_REG_BITS == 64 */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* As user-mode emulation typically means running multiple instances
|
||||
* of the translator don't go too nuts with our default code gen
|
||||
* buffer lest we make things too hard for the OS.
|
||||
*/
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (128 * MiB)
|
||||
#else
|
||||
/*
|
||||
* We expect most system emulation to run one or two guests per host.
|
||||
* Users running large scale system emulation may want to tweak their
|
||||
* runtime setup via the tb-size control on the command line.
|
||||
*/
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (1 * GiB)
|
||||
#endif
|
||||
#endif
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32u * 1024 * 1024)
|
||||
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE \
|
||||
(DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \
|
||||
@@ -976,12 +937,15 @@ static inline size_t size_code_gen_buffer(size_t tb_size)
|
||||
{
|
||||
/* Size the buffer. */
|
||||
if (tb_size == 0) {
|
||||
size_t phys_mem = qemu_get_host_physmem();
|
||||
if (phys_mem == 0) {
|
||||
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
|
||||
} else {
|
||||
tb_size = MIN(DEFAULT_CODE_GEN_BUFFER_SIZE, phys_mem / 8);
|
||||
}
|
||||
#ifdef USE_STATIC_CODE_GEN_BUFFER
|
||||
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
|
||||
#else
|
||||
/* ??? Needs adjustments. */
|
||||
/* ??? If we relax the requirement that CONFIG_USER_ONLY use the
|
||||
static buffer, we could size this on RESERVED_VA, on the text
|
||||
segment size of the executable, or continue to use the default. */
|
||||
tb_size = (unsigned long)(ram_size / 4);
|
||||
#endif
|
||||
}
|
||||
if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) {
|
||||
tb_size = MIN_CODE_GEN_BUFFER_SIZE;
|
||||
@@ -1068,20 +1032,47 @@ static inline void *alloc_code_gen_buffer(void)
|
||||
{
|
||||
int prot = PROT_WRITE | PROT_READ | PROT_EXEC;
|
||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
uintptr_t start = 0;
|
||||
size_t size = tcg_ctx->code_gen_buffer_size;
|
||||
void *buf;
|
||||
|
||||
buf = mmap(NULL, size, prot, flags, -1, 0);
|
||||
/* Constrain the position of the buffer based on the host cpu.
|
||||
Note that these addresses are chosen in concert with the
|
||||
addresses assigned in the relevant linker script file. */
|
||||
# if defined(__PIE__) || defined(__PIC__)
|
||||
/* Don't bother setting a preferred location if we're building
|
||||
a position-independent executable. We're more likely to get
|
||||
an address near the main executable if we let the kernel
|
||||
choose the address. */
|
||||
# elif defined(__x86_64__) && defined(MAP_32BIT)
|
||||
/* Force the memory down into low memory with the executable.
|
||||
Leave the choice of exact location with the kernel. */
|
||||
flags |= MAP_32BIT;
|
||||
/* Cannot expect to map more than 800MB in low memory. */
|
||||
if (size > 800u * 1024 * 1024) {
|
||||
tcg_ctx->code_gen_buffer_size = size = 800u * 1024 * 1024;
|
||||
}
|
||||
# elif defined(__sparc__)
|
||||
start = 0x40000000ul;
|
||||
# elif defined(__s390x__)
|
||||
start = 0x90000000ul;
|
||||
# elif defined(__mips__)
|
||||
# if _MIPS_SIM == _ABI64
|
||||
start = 0x128000000ul;
|
||||
# else
|
||||
start = 0x08000000ul;
|
||||
# endif
|
||||
# endif
|
||||
|
||||
buf = mmap((void *)start, size, prot, flags, -1, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
if (cross_256mb(buf, size)) {
|
||||
/*
|
||||
* Try again, with the original still mapped, to avoid re-acquiring
|
||||
* the same 256mb crossing.
|
||||
*/
|
||||
/* Try again, with the original still mapped, to avoid re-acquiring
|
||||
that 256mb crossing. This time don't specify an address. */
|
||||
size_t size2;
|
||||
void *buf2 = mmap(NULL, size, prot, flags, -1, 0);
|
||||
switch ((int)(buf2 != MAP_FAILED)) {
|
||||
@@ -1165,6 +1156,23 @@ void tcg_exec_init(unsigned long tb_size)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new translation block. Flush the translation buffer if
|
||||
* too many translation blocks or too much generated code.
|
||||
*/
|
||||
static TranslationBlock *tb_alloc(target_ulong pc)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
|
||||
assert_memory_lock();
|
||||
|
||||
tb = tcg_tb_alloc(tcg_ctx);
|
||||
if (unlikely(tb == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return tb;
|
||||
}
|
||||
|
||||
/* call with @p->lock held */
|
||||
static inline void invalidate_page_bitmap(PageDesc *p)
|
||||
{
|
||||
@@ -1223,8 +1231,6 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
|
||||
/* flush all the translation blocks */
|
||||
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||
{
|
||||
bool did_flush = false;
|
||||
|
||||
mmap_lock();
|
||||
/* If it is already been done on request of another CPU,
|
||||
* just retry.
|
||||
@@ -1232,7 +1238,6 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
|
||||
goto done;
|
||||
}
|
||||
did_flush = true;
|
||||
|
||||
if (DEBUG_TB_FLUSH_GATE) {
|
||||
size_t nb_tbs = tcg_nb_tbs();
|
||||
@@ -1257,22 +1262,14 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||
|
||||
done:
|
||||
mmap_unlock();
|
||||
if (did_flush) {
|
||||
qemu_plugin_flush_cb();
|
||||
}
|
||||
}
|
||||
|
||||
void tb_flush(CPUState *cpu)
|
||||
{
|
||||
if (tcg_enabled()) {
|
||||
unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
|
||||
|
||||
if (cpu_in_exclusive_context(cpu)) {
|
||||
do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
|
||||
} else {
|
||||
async_safe_run_on_cpu(cpu, do_tb_flush,
|
||||
RUN_ON_CPU_HOST_INT(tb_flush_count));
|
||||
}
|
||||
async_safe_run_on_cpu(cpu, do_tb_flush,
|
||||
RUN_ON_CPU_HOST_INT(tb_flush_count));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1684,7 +1681,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
TCGProfile *prof = &tcg_ctx->prof;
|
||||
int64_t ti;
|
||||
#endif
|
||||
|
||||
assert_memory_lock();
|
||||
|
||||
phys_pc = get_page_addr_code(env, pc);
|
||||
@@ -1710,7 +1706,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
}
|
||||
|
||||
buffer_overflow:
|
||||
tb = tcg_tb_alloc(tcg_ctx);
|
||||
tb = tb_alloc(pc);
|
||||
if (unlikely(!tb)) {
|
||||
/* flush must be done */
|
||||
tb_flush(cpu);
|
||||
@@ -1726,7 +1722,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
tb->cs_base = cs_base;
|
||||
tb->flags = flags;
|
||||
tb->cflags = cflags;
|
||||
tb->orig_tb = NULL;
|
||||
tb->trace_vcpu_dstate = *cpu->trace_dstate;
|
||||
tcg_ctx->tb_cflags = cflags;
|
||||
tb_overflow:
|
||||
@@ -1813,44 +1808,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
#ifdef DEBUG_DISAS
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
|
||||
qemu_log_in_addr_range(tb->pc)) {
|
||||
FILE *logfile = qemu_log_lock();
|
||||
int code_size, data_size = 0;
|
||||
g_autoptr(GString) note = g_string_new("[tb header & initial instruction]");
|
||||
size_t chunk_start = 0;
|
||||
int insn = 0;
|
||||
qemu_log_lock();
|
||||
qemu_log("OUT: [size=%d]\n", gen_code_size);
|
||||
if (tcg_ctx->data_gen_ptr) {
|
||||
code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr;
|
||||
data_size = gen_code_size - code_size;
|
||||
} else {
|
||||
code_size = gen_code_size;
|
||||
}
|
||||
size_t code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr;
|
||||
size_t data_size = gen_code_size - code_size;
|
||||
size_t i;
|
||||
|
||||
/* Dump header and the first instruction */
|
||||
chunk_start = tcg_ctx->gen_insn_end_off[insn];
|
||||
log_disas(tb->tc.ptr, chunk_start, note->str);
|
||||
log_disas(tb->tc.ptr, code_size);
|
||||
|
||||
/*
|
||||
* Dump each instruction chunk, wrapping up empty chunks into
|
||||
* the next instruction. The whole array is offset so the
|
||||
* first entry is the beginning of the 2nd instruction.
|
||||
*/
|
||||
while (insn <= tb->icount && chunk_start < code_size) {
|
||||
size_t chunk_end = tcg_ctx->gen_insn_end_off[insn];
|
||||
if (chunk_end > chunk_start) {
|
||||
g_string_printf(note, "[guest addr: " TARGET_FMT_lx "]",
|
||||
tcg_ctx->gen_insn_data[insn][0]);
|
||||
log_disas(tb->tc.ptr + chunk_start, chunk_end - chunk_start,
|
||||
note->str);
|
||||
chunk_start = chunk_end;
|
||||
}
|
||||
insn++;
|
||||
}
|
||||
|
||||
/* Finally dump any data we may have after the block */
|
||||
if (data_size) {
|
||||
int i;
|
||||
qemu_log(" data: [size=%d]\n", data_size);
|
||||
for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) {
|
||||
if (sizeof(tcg_target_ulong) == 8) {
|
||||
qemu_log("0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n",
|
||||
@@ -1862,10 +1828,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
*(uint32_t *)(tcg_ctx->data_gen_ptr + i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_disas(tb->tc.ptr, gen_code_size);
|
||||
}
|
||||
qemu_log("\n");
|
||||
qemu_log_flush();
|
||||
qemu_log_unlock(logfile);
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1906,7 +1874,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize);
|
||||
atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned);
|
||||
tb_destroy(tb);
|
||||
return existing_tb;
|
||||
}
|
||||
tcg_tb_insert(tb);
|
||||
@@ -1922,7 +1889,7 @@ static void
|
||||
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
||||
PageDesc *p, tb_page_addr_t start,
|
||||
tb_page_addr_t end,
|
||||
uintptr_t retaddr)
|
||||
int is_cpu_write_access)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
tb_page_addr_t tb_start, tb_end;
|
||||
@@ -1930,9 +1897,9 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
CPUState *cpu = current_cpu;
|
||||
CPUArchState *env = NULL;
|
||||
bool current_tb_not_found = retaddr != 0;
|
||||
bool current_tb_modified = false;
|
||||
int current_tb_not_found = is_cpu_write_access;
|
||||
TranslationBlock *current_tb = NULL;
|
||||
int current_tb_modified = 0;
|
||||
target_ulong current_pc = 0;
|
||||
target_ulong current_cs_base = 0;
|
||||
uint32_t current_flags = 0;
|
||||
@@ -1964,21 +1931,24 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
||||
if (!(tb_end <= start || tb_start >= end)) {
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
if (current_tb_not_found) {
|
||||
current_tb_not_found = false;
|
||||
/* now we have a real cpu fault */
|
||||
current_tb = tcg_tb_lookup(retaddr);
|
||||
current_tb_not_found = 0;
|
||||
current_tb = NULL;
|
||||
if (cpu->mem_io_pc) {
|
||||
/* now we have a real cpu fault */
|
||||
current_tb = tcg_tb_lookup(cpu->mem_io_pc);
|
||||
}
|
||||
}
|
||||
if (current_tb == tb &&
|
||||
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
|
||||
/*
|
||||
* If we are modifying the current TB, we must stop
|
||||
* its execution. We could be more precise by checking
|
||||
* that the modification is after the current PC, but it
|
||||
* would require a specialized function to partially
|
||||
* restore the CPU state.
|
||||
*/
|
||||
current_tb_modified = true;
|
||||
cpu_restore_state_from_tb(cpu, current_tb, retaddr, true);
|
||||
/* If we are modifying the current TB, we must stop
|
||||
its execution. We could be more precise by checking
|
||||
that the modification is after the current PC, but it
|
||||
would require a specialized function to partially
|
||||
restore the CPU state */
|
||||
|
||||
current_tb_modified = 1;
|
||||
cpu_restore_state_from_tb(cpu, current_tb,
|
||||
cpu->mem_io_pc, true);
|
||||
cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base,
|
||||
¤t_flags);
|
||||
}
|
||||
@@ -2013,7 +1983,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
||||
*
|
||||
* Called with mmap_lock held for user-mode emulation
|
||||
*/
|
||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
|
||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
|
||||
int is_cpu_write_access)
|
||||
{
|
||||
struct page_collection *pages;
|
||||
PageDesc *p;
|
||||
@@ -2025,7 +1996,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
|
||||
return;
|
||||
}
|
||||
pages = page_collection_lock(start, end);
|
||||
tb_invalidate_phys_page_range__locked(pages, p, start, end, 0);
|
||||
tb_invalidate_phys_page_range__locked(pages, p, start, end,
|
||||
is_cpu_write_access);
|
||||
page_collection_unlock(pages);
|
||||
}
|
||||
|
||||
@@ -2072,8 +2044,7 @@ void tb_invalidate_phys_range(target_ulong start, target_ulong end)
|
||||
* Call with all @pages in the range [@start, @start + len[ locked.
|
||||
*/
|
||||
void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
tb_page_addr_t start, int len,
|
||||
uintptr_t retaddr)
|
||||
tb_page_addr_t start, int len)
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
@@ -2100,8 +2071,7 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
}
|
||||
} else {
|
||||
do_invalidate:
|
||||
tb_invalidate_phys_page_range__locked(pages, p, start, start + len,
|
||||
retaddr);
|
||||
tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -2175,16 +2145,16 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc)
|
||||
#endif
|
||||
|
||||
/* user-mode: call with mmap_lock held */
|
||||
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
|
||||
void tb_check_watchpoint(CPUState *cpu)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
|
||||
assert_memory_lock();
|
||||
|
||||
tb = tcg_tb_lookup(retaddr);
|
||||
tb = tcg_tb_lookup(cpu->mem_io_pc);
|
||||
if (tb) {
|
||||
/* We can use retranslation to find the PC. */
|
||||
cpu_restore_state_from_tb(cpu, tb, retaddr, true);
|
||||
cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc, true);
|
||||
tb_phys_invalidate(tb, -1);
|
||||
} else {
|
||||
/* The exception probably happened in a helper. The CPU state should
|
||||
@@ -2256,7 +2226,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
tb_phys_invalidate(tb->orig_tb, -1);
|
||||
}
|
||||
tcg_tb_remove(tb);
|
||||
tb_destroy(tb);
|
||||
}
|
||||
|
||||
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
|
||||
@@ -2551,7 +2520,9 @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
|
||||
/* This function should never be called with addresses outside the
|
||||
guest address space. If this assert fires, it probably indicates
|
||||
a missing call to h2g_valid. */
|
||||
assert(end - 1 <= GUEST_ADDR_MAX);
|
||||
#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
|
||||
assert(end <= ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
|
||||
#endif
|
||||
assert(start < end);
|
||||
assert_memory_lock();
|
||||
|
||||
@@ -2587,9 +2558,9 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
|
||||
/* This function should never be called with addresses outside the
|
||||
guest address space. If this assert fires, it probably indicates
|
||||
a missing call to h2g_valid. */
|
||||
if (TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS) {
|
||||
assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
|
||||
}
|
||||
#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
|
||||
assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
|
||||
#endif
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
|
||||
@@ -27,10 +27,10 @@ struct page_collection *page_collection_lock(tb_page_addr_t start,
|
||||
tb_page_addr_t end);
|
||||
void page_collection_unlock(struct page_collection *set);
|
||||
void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
tb_page_addr_t start, int len,
|
||||
uintptr_t retaddr);
|
||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end);
|
||||
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
|
||||
tb_page_addr_t start, int len);
|
||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
|
||||
int is_cpu_write_access);
|
||||
void tb_check_watchpoint(CPUState *cpu);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int page_unprotect(target_ulong address, uintptr_t pc);
|
||||
|
||||
+3
-22
@@ -16,7 +16,6 @@
|
||||
#include "exec/gen-icount.h"
|
||||
#include "exec/log.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
|
||||
/* Pairs with tcg_clear_temp_count.
|
||||
To be called by #TranslatorOps.{translate_insn,tb_stop} if
|
||||
@@ -35,7 +34,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb, int max_insns)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
bool plugin_enabled;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
@@ -57,17 +55,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
ops->tb_start(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
plugin_enabled = plugin_gen_tb_start(cpu, tb);
|
||||
|
||||
while (true) {
|
||||
db->num_insns++;
|
||||
ops->insn_start(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_insn_start(cpu, db);
|
||||
}
|
||||
|
||||
/* Pass breakpoint hits to target for further processing */
|
||||
if (!db->singlestep_enabled
|
||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
@@ -98,6 +90,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
/* Accept I/O on the last instruction. */
|
||||
gen_io_start();
|
||||
ops->translate_insn(db, cpu);
|
||||
gen_io_end();
|
||||
} else {
|
||||
ops->translate_insn(db, cpu);
|
||||
}
|
||||
@@ -107,14 +100,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
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) {
|
||||
@@ -127,10 +112,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_tb_end(cpu);
|
||||
}
|
||||
|
||||
/* The disas_log hook may use these values rather than recompute. */
|
||||
db->tb->size = db->pc_next - db->pc_first;
|
||||
db->tb->icount = db->num_insns;
|
||||
@@ -138,11 +119,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
#ifdef DEBUG_DISAS
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
|
||||
&& qemu_log_in_addr_range(db->pc_first)) {
|
||||
FILE *logfile = qemu_log_lock();
|
||||
qemu_log_lock();
|
||||
qemu_log("----------------\n");
|
||||
ops->disas_log(db, cpu);
|
||||
qemu_log("\n");
|
||||
qemu_log_unlock(logfile);
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
|
||||
+4
-472
@@ -20,14 +20,12 @@
|
||||
#include "cpu.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "translate-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace/mem.h"
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
@@ -190,63 +188,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static int probe_access_internal(CPUArchState *env, target_ulong addr,
|
||||
int fault_size, MMUAccessType access_type,
|
||||
bool nonfault, uintptr_t ra)
|
||||
{
|
||||
int flags;
|
||||
|
||||
switch (access_type) {
|
||||
case MMU_DATA_STORE:
|
||||
flags = PAGE_WRITE;
|
||||
break;
|
||||
case MMU_DATA_LOAD:
|
||||
flags = PAGE_READ;
|
||||
break;
|
||||
case MMU_INST_FETCH:
|
||||
flags = PAGE_EXEC;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (!guest_addr_valid(addr) || page_check_range(addr, 1, flags) < 0) {
|
||||
if (nonfault) {
|
||||
return TLB_INVALID_MASK;
|
||||
} else {
|
||||
CPUState *cpu = env_cpu(env);
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
cc->tlb_fill(cpu, addr, fault_size, access_type,
|
||||
MMU_USER_IDX, false, ra);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int probe_access_flags(CPUArchState *env, target_ulong addr,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool nonfault, void **phost, uintptr_t ra)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = probe_access_internal(env, addr, 0, access_type, nonfault, ra);
|
||||
*phost = flags ? NULL : g2h(addr);
|
||||
return flags;
|
||||
}
|
||||
|
||||
void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
||||
MMUAccessType access_type, int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
int flags;
|
||||
|
||||
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
|
||||
flags = probe_access_internal(env, addr, size, access_type, false, ra);
|
||||
g_assert(flags == 0);
|
||||
|
||||
return size ? g2h(addr) : NULL;
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
@@ -517,7 +458,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <ucontext.h>
|
||||
#include <sys/siginfo.h>
|
||||
#endif
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
@@ -526,12 +466,10 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
siginfo_t *info = pinfo;
|
||||
#if defined(__NetBSD__)
|
||||
ucontext_t *uc = puc;
|
||||
siginfo_t *si = pinfo;
|
||||
#else
|
||||
ucontext_t *uc = puc;
|
||||
#endif
|
||||
unsigned long pc;
|
||||
uint32_t fsr;
|
||||
int is_write;
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
@@ -542,48 +480,15 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
pc = uc->uc_mcontext.arm_pc;
|
||||
#endif
|
||||
|
||||
#ifdef __NetBSD__
|
||||
fsr = si->si_trap;
|
||||
#else
|
||||
fsr = uc->uc_mcontext.error_code;
|
||||
#endif
|
||||
/*
|
||||
* In the FSR, bit 11 is WnR, assuming a v6 or
|
||||
* later processor. On v5 we will always report
|
||||
* this as a read, which will fail later.
|
||||
/* error_code is the FSR value, in which bit 11 is WnR (assuming a v6 or
|
||||
* later processor; on v5 we will always report this as a read).
|
||||
*/
|
||||
is_write = extract32(fsr, 11, 1);
|
||||
is_write = extract32(uc->uc_mcontext.error_code, 11, 1);
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
|
||||
#include <ucontext.h>
|
||||
#include <sys/siginfo.h>
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
||||
{
|
||||
ucontext_t *uc = puc;
|
||||
siginfo_t *si = pinfo;
|
||||
unsigned long pc;
|
||||
int is_write;
|
||||
uint32_t esr;
|
||||
|
||||
pc = uc->uc_mcontext.__gregs[_REG_PC];
|
||||
esr = si->si_trap;
|
||||
|
||||
/*
|
||||
* siginfo_t::si_trap is the ESR value, for data aborts ESR.EC
|
||||
* is 0b10010x: then bit 6 is the WnR bit
|
||||
*/
|
||||
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
|
||||
return handle_cpu_signal(pc, si, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ESR_MAGIC
|
||||
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
|
||||
#define ESR_MAGIC 0x45535201
|
||||
@@ -646,7 +551,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
||||
}
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(__s390__)
|
||||
|
||||
@@ -798,375 +702,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
|
||||
/* The softmmu versions of these helpers are in cputlb.c. */
|
||||
|
||||
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldub_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
int ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_SB, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldsb_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BEUW, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = lduw_be_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
int ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BESW, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldsw_be_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BEUL, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldl_be_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint64_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BEQ, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldq_be_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LEUW, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = lduw_le_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
int ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LESW, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldsw_le_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LEUL, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldl_le_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint64_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LEQ, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldq_le_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldub_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldsb_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_lduw_be_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldsw_be_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldl_be_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldq_be_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_lduw_le_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldsw_le_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldl_le_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldq_le_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stb_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BEUW, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stw_be_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BEUL, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stl_be_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_BEQ, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stq_be_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LEUW, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stw_le_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LEUL, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stl_le_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_LEQ, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stq_le_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stb_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stw_be_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stl_be_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint64_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stq_be_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stw_le_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stl_le_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint64_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stq_le_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldub_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = lduw_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldl_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldq_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do not allow unaligned operations to proceed. Return the host address. */
|
||||
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
int size, uintptr_t retaddr)
|
||||
@@ -1184,13 +719,10 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
#define ATOMIC_MMU_DECLS do {} while (0)
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
|
||||
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
|
||||
#define ATOMIC_MMU_IDX MMU_USER_IDX
|
||||
|
||||
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
|
||||
#define EXTRA_ARGS
|
||||
|
||||
#include "atomic_common.inc.c"
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "atomic_template.h"
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += xen-all.o
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/soundhw.h"
|
||||
#include "qapi/qapi-commands-misc.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -37,10 +38,6 @@
|
||||
int graphic_width = 1024;
|
||||
int graphic_height = 768;
|
||||
int graphic_depth = 8;
|
||||
#elif defined(TARGET_M68K)
|
||||
int graphic_width = 800;
|
||||
int graphic_height = 600;
|
||||
int graphic_depth = 8;
|
||||
#else
|
||||
int graphic_width = 800;
|
||||
int graphic_height = 600;
|
||||
@@ -76,8 +73,6 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_PPC
|
||||
#elif defined(TARGET_RISCV)
|
||||
#define QEMU_ARCH QEMU_ARCH_RISCV
|
||||
#elif defined(TARGET_RX)
|
||||
#define QEMU_ARCH QEMU_ARCH_RX
|
||||
#elif defined(TARGET_S390X)
|
||||
#define QEMU_ARCH QEMU_ARCH_S390X
|
||||
#elif defined(TARGET_SH4)
|
||||
@@ -90,8 +85,6 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#elif defined(TARGET_XTENSA)
|
||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
||||
#elif defined(TARGET_AVR)
|
||||
#define QEMU_ARCH QEMU_ARCH_AVR
|
||||
#endif
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
@@ -113,3 +106,14 @@ int xen_available(void)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TargetInfo *qmp_query_target(Error **errp)
|
||||
{
|
||||
TargetInfo *info = g_malloc0(sizeof(*info));
|
||||
|
||||
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
|
||||
&error_abort);
|
||||
|
||||
return info;
|
||||
}
|
||||
+1
-5
@@ -2,6 +2,7 @@ common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
|
||||
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
|
||||
common-obj-y += wavcapture.o
|
||||
|
||||
@@ -28,8 +29,3 @@ common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo
|
||||
sdl.mo-objs = sdlaudio.o
|
||||
sdl.mo-cflags := $(SDL_CFLAGS)
|
||||
sdl.mo-libs := $(SDL_LIBS)
|
||||
|
||||
# jack module
|
||||
common-obj-$(CONFIG_AUDIO_JACK) += jack.mo
|
||||
jack.mo-objs = jackaudio.o
|
||||
jack.mo-libs := $(JACK_LIBS)
|
||||
+285
-134
@@ -39,11 +39,13 @@ struct pollhlp {
|
||||
struct pollfd *pfds;
|
||||
int count;
|
||||
int mask;
|
||||
AudioState *s;
|
||||
};
|
||||
|
||||
typedef struct ALSAVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int wpos;
|
||||
int pending;
|
||||
void *pcm_buf;
|
||||
snd_pcm_t *handle;
|
||||
struct pollhlp pollhlp;
|
||||
Audiodev *dev;
|
||||
@@ -52,6 +54,7 @@ typedef struct ALSAVoiceOut {
|
||||
typedef struct ALSAVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
snd_pcm_t *handle;
|
||||
void *pcm_buf;
|
||||
struct pollhlp pollhlp;
|
||||
Audiodev *dev;
|
||||
} ALSAVoiceIn;
|
||||
@@ -196,11 +199,11 @@ static void alsa_poll_handler (void *opaque)
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
audio_run(hlp->s, "alsa run (prepared)");
|
||||
audio_run ("alsa run (prepared)");
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_RUNNING:
|
||||
audio_run(hlp->s, "alsa run (running)");
|
||||
audio_run ("alsa run (running)");
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -266,6 +269,11 @@ static int alsa_poll_in (HWVoiceIn *hw)
|
||||
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
|
||||
}
|
||||
|
||||
static int alsa_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
{
|
||||
switch (fmt) {
|
||||
@@ -307,13 +315,6 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
return SND_PCM_FORMAT_U32_LE;
|
||||
}
|
||||
|
||||
case AUDIO_FORMAT_F32:
|
||||
if (endianness) {
|
||||
return SND_PCM_FORMAT_FLOAT_BE;
|
||||
} else {
|
||||
return SND_PCM_FORMAT_FLOAT_LE;
|
||||
}
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
@@ -377,16 +378,6 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
|
||||
*fmt = AUDIO_FORMAT_U32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_FLOAT_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_FLOAT_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized audio format %d\n", alsafmt);
|
||||
return -1;
|
||||
@@ -510,6 +501,13 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Can not handle obtained number of channels %d\n",
|
||||
nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (apdo->buffer_length) {
|
||||
int dir = 0;
|
||||
unsigned int btime = apdo->buffer_length;
|
||||
@@ -608,64 +606,102 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
size_t pos = 0;
|
||||
size_t len_frames = len / hw->info.bytes_per_frame;
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
while (len_frames) {
|
||||
char *src = advance(buf, pos);
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
written = snd_pcm_writei(alsa->handle, src, len_frames);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
trace_alsa_wrote_zero(len_frames);
|
||||
return pos;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover(alsa->handle)) {
|
||||
alsa_logerr(written, "Failed to write %zu frames\n",
|
||||
len_frames);
|
||||
return pos;
|
||||
}
|
||||
trace_alsa_xrun_out();
|
||||
continue;
|
||||
|
||||
case -ESTRPIPE:
|
||||
/*
|
||||
* stream is suspended and waiting for an application
|
||||
* recovery
|
||||
*/
|
||||
if (alsa_resume(alsa->handle)) {
|
||||
alsa_logerr(written, "Failed to write %zu frames\n",
|
||||
len_frames);
|
||||
return pos;
|
||||
}
|
||||
trace_alsa_resume_out();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return pos;
|
||||
|
||||
default:
|
||||
alsa_logerr(written, "Failed to write %zu frames from %p\n",
|
||||
len, src);
|
||||
return pos;
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
if (avail < 0) {
|
||||
if (avail == -EPIPE) {
|
||||
if (!alsa_recover (handle)) {
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
}
|
||||
}
|
||||
|
||||
pos += written * hw->info.bytes_per_frame;
|
||||
if (written < len_frames) {
|
||||
break;
|
||||
if (avail < 0) {
|
||||
alsa_logerr (avail,
|
||||
"Could not obtain number of available frames\n");
|
||||
return -1;
|
||||
}
|
||||
len_frames -= written;
|
||||
}
|
||||
|
||||
return pos;
|
||||
return avail;
|
||||
}
|
||||
|
||||
static void alsa_write_pending (ALSAVoiceOut *alsa)
|
||||
{
|
||||
HWVoiceOut *hw = &alsa->hw;
|
||||
|
||||
while (alsa->pending) {
|
||||
int left_till_end_samples = hw->samples - alsa->wpos;
|
||||
int len = audio_MIN (alsa->pending, left_till_end_samples);
|
||||
char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
|
||||
|
||||
while (len) {
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
written = snd_pcm_writei (alsa->handle, src, len);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
trace_alsa_wrote_zero(len);
|
||||
return;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
trace_alsa_xrun_out();
|
||||
continue;
|
||||
|
||||
case -ESTRPIPE:
|
||||
/* stream is suspended and waiting for an
|
||||
application recovery */
|
||||
if (alsa_resume (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
trace_alsa_resume_out();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return;
|
||||
|
||||
default:
|
||||
alsa_logerr (written, "Failed to write %d frames from %p\n",
|
||||
len, src);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alsa->wpos = (alsa->wpos + written) % hw->samples;
|
||||
alsa->pending -= written;
|
||||
len -= written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
int decr;
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of available playback frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (live, avail);
|
||||
decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
|
||||
alsa->pending += decr;
|
||||
alsa_write_pending (alsa);
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void alsa_fini_out (HWVoiceOut *hw)
|
||||
@@ -674,6 +710,9 @@ static void alsa_fini_out (HWVoiceOut *hw)
|
||||
|
||||
ldebug ("alsa_fini\n");
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
|
||||
g_free(alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
@@ -702,7 +741,14 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pollhlp.s = hw->s;
|
||||
alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close1 (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
alsa->dev = dev;
|
||||
return 0;
|
||||
@@ -741,28 +787,34 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alsa_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
|
||||
|
||||
if (enable) {
|
||||
bool poll_mode = apdo->try_poll;
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = apdo->try_poll;
|
||||
|
||||
ldebug("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_out(hw)) {
|
||||
poll_mode = 0;
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_out (hw)) {
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
|
||||
} else {
|
||||
ldebug("disabling voice\n");
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
alsa_fini_poll(&alsa->pollhlp);
|
||||
alsa_fini_poll (&alsa->pollhlp);
|
||||
}
|
||||
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
|
||||
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
@@ -790,7 +842,14 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pollhlp.s = hw->s;
|
||||
alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close1 (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
alsa->dev = dev;
|
||||
return 0;
|
||||
@@ -801,74 +860,165 @@ static void alsa_fini_in (HWVoiceIn *hw)
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
|
||||
g_free(alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
static int alsa_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
size_t pos = 0;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int decr;
|
||||
struct {
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ .add = hw->wpos, .len = 0 },
|
||||
{ .add = 0, .len = 0 }
|
||||
};
|
||||
snd_pcm_sframes_t avail;
|
||||
snd_pcm_uframes_t read_samples = 0;
|
||||
|
||||
while (len) {
|
||||
void *dst = advance(buf, pos);
|
||||
snd_pcm_sframes_t nread;
|
||||
|
||||
nread = snd_pcm_readi(
|
||||
alsa->handle, dst, len / hw->info.bytes_per_frame);
|
||||
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
trace_alsa_read_zero(len);
|
||||
return pos;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover(alsa->handle)) {
|
||||
alsa_logerr(nread, "Failed to read %zu frames\n", len);
|
||||
return pos;
|
||||
}
|
||||
trace_alsa_xrun_in();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return pos;
|
||||
|
||||
default:
|
||||
alsa_logerr(nread, "Failed to read %zu frames to %p\n",
|
||||
len, dst);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos += nread * hw->info.bytes_per_frame;
|
||||
len -= nread * hw->info.bytes_per_frame;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pos;
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of captured frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!avail) {
|
||||
snd_pcm_state_t state;
|
||||
|
||||
state = snd_pcm_state (alsa->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
avail = hw->samples;
|
||||
break;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
/* stream is suspended and waiting for an application recovery */
|
||||
if (alsa_resume (alsa->handle)) {
|
||||
dolog ("Failed to resume suspended input stream\n");
|
||||
return 0;
|
||||
}
|
||||
trace_alsa_resume_in();
|
||||
break;
|
||||
default:
|
||||
trace_alsa_no_frames(state);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
decr = audio_MIN (dead, avail);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + decr > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos);
|
||||
bufs[1].len = (decr - (hw->samples - hw->wpos));
|
||||
}
|
||||
else {
|
||||
bufs[0].len = decr;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
void *src;
|
||||
struct st_sample *dst;
|
||||
snd_pcm_sframes_t nread;
|
||||
snd_pcm_uframes_t len;
|
||||
|
||||
len = bufs[i].len;
|
||||
|
||||
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
|
||||
dst = hw->conv_buf + bufs[i].add;
|
||||
|
||||
while (len) {
|
||||
nread = snd_pcm_readi (alsa->handle, src, len);
|
||||
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
trace_alsa_read_zero(len);
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (nread, "Failed to read %ld frames\n", len);
|
||||
goto exit;
|
||||
}
|
||||
trace_alsa_xrun_in();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (
|
||||
nread,
|
||||
"Failed to read %ld frames from %p\n",
|
||||
len,
|
||||
src
|
||||
);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
hw->conv (dst, src, nread);
|
||||
|
||||
src = advance (src, nread << hwshift);
|
||||
dst += nread;
|
||||
|
||||
read_samples += nread;
|
||||
len -= nread;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
}
|
||||
|
||||
static void alsa_enable_in(HWVoiceIn *hw, bool enable)
|
||||
static int alsa_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
|
||||
|
||||
if (enable) {
|
||||
bool poll_mode = apdo->try_poll;
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = apdo->try_poll;
|
||||
|
||||
ldebug("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_in(hw)) {
|
||||
poll_mode = 0;
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_in (hw)) {
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
|
||||
} else {
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
alsa_fini_poll(&alsa->pollhlp);
|
||||
alsa_fini_poll (&alsa->pollhlp);
|
||||
}
|
||||
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
|
||||
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
|
||||
@@ -922,14 +1072,15 @@ static void alsa_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
.run_out = alsa_run_out,
|
||||
.write = alsa_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = alsa_enable_out,
|
||||
.ctl_out = alsa_ctl_out,
|
||||
|
||||
.init_in = alsa_init_in,
|
||||
.fini_in = alsa_fini_in,
|
||||
.run_in = alsa_run_in,
|
||||
.read = alsa_read,
|
||||
.enable_in = alsa_enable_in,
|
||||
.ctl_in = alsa_ctl_in,
|
||||
};
|
||||
|
||||
static struct audio_driver alsa_audio_driver = {
|
||||
|
||||
+249
-619
File diff suppressed because it is too large.
Load diff
+24
-27
@@ -27,7 +27,6 @@
|
||||
|
||||
#include "qemu/queue.h"
|
||||
#include "qapi/qapi-types-audio.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
||||
|
||||
@@ -60,7 +59,7 @@ typedef enum {
|
||||
|
||||
struct audio_capture_ops {
|
||||
void (*notify) (void *opaque, audcnotification_e cmd);
|
||||
void (*capture) (void *opaque, const void *buf, int size);
|
||||
void (*capture) (void *opaque, void *buf, int size);
|
||||
void (*destroy) (void *opaque);
|
||||
};
|
||||
|
||||
@@ -79,10 +78,8 @@ typedef struct SWVoiceOut SWVoiceOut;
|
||||
typedef struct CaptureVoiceOut CaptureVoiceOut;
|
||||
typedef struct SWVoiceIn SWVoiceIn;
|
||||
|
||||
typedef struct AudioState AudioState;
|
||||
typedef struct QEMUSoundCard {
|
||||
char *name;
|
||||
AudioState *state;
|
||||
QLIST_ENTRY (QEMUSoundCard) entries;
|
||||
} QEMUSoundCard;
|
||||
|
||||
@@ -95,8 +92,7 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
||||
|
||||
void AUD_register_card (const char *name, QEMUSoundCard *card);
|
||||
void AUD_remove_card (QEMUSoundCard *card);
|
||||
CaptureVoiceOut *AUD_add_capture(
|
||||
AudioState *s,
|
||||
CaptureVoiceOut *AUD_add_capture (
|
||||
struct audsettings *as,
|
||||
struct audio_capture_ops *ops,
|
||||
void *opaque
|
||||
@@ -113,7 +109,7 @@ SWVoiceOut *AUD_open_out (
|
||||
);
|
||||
|
||||
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
|
||||
size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
|
||||
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
|
||||
int AUD_get_buffer_size_out (SWVoiceOut *sw);
|
||||
void AUD_set_active_out (SWVoiceOut *sw, int on);
|
||||
int AUD_is_active_out (SWVoiceOut *sw);
|
||||
@@ -124,16 +120,6 @@ uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
|
||||
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
|
||||
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
|
||||
|
||||
#define AUDIO_MAX_CHANNELS 16
|
||||
typedef struct Volume {
|
||||
bool mute;
|
||||
int channels;
|
||||
uint8_t vol[AUDIO_MAX_CHANNELS];
|
||||
} Volume;
|
||||
|
||||
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol);
|
||||
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol);
|
||||
|
||||
SWVoiceIn *AUD_open_in (
|
||||
QEMUSoundCard *card,
|
||||
SWVoiceIn *sw,
|
||||
@@ -144,7 +130,7 @@ SWVoiceIn *AUD_open_in (
|
||||
);
|
||||
|
||||
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
|
||||
size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
|
||||
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
|
||||
void AUD_set_active_in (SWVoiceIn *sw, int on);
|
||||
int AUD_is_active_in (SWVoiceIn *sw);
|
||||
|
||||
@@ -157,13 +143,30 @@ static inline void *advance (void *p, int incr)
|
||||
return (d + incr);
|
||||
}
|
||||
|
||||
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
|
||||
int freq, int bits, int nchannels);
|
||||
#ifdef __GNUC__
|
||||
#define audio_MIN(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
__typeof (b) tb = b; \
|
||||
((ta)>(tb)?(tb):(ta)); \
|
||||
}))
|
||||
|
||||
#define audio_MAX(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
__typeof (b) tb = b; \
|
||||
((ta)<(tb)?(tb):(ta)); \
|
||||
}))
|
||||
#else
|
||||
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
|
||||
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
|
||||
#endif
|
||||
|
||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
int bits, int nchannels);
|
||||
|
||||
bool audio_is_cleaning_up(void);
|
||||
void audio_cleanup(void);
|
||||
|
||||
void audio_sample_to_uint64(const void *samples, int pos,
|
||||
void audio_sample_to_uint64(void *samples, int pos,
|
||||
uint64_t *left, uint64_t *right);
|
||||
void audio_sample_from_uint64(void *samples, int pos,
|
||||
uint64_t left, uint64_t right);
|
||||
@@ -172,10 +175,4 @@ void audio_parse_option(const char *opt);
|
||||
void audio_init_audiodevs(void);
|
||||
void audio_legacy_help(void);
|
||||
|
||||
AudioState *audio_state_by_name(const char *name);
|
||||
const char *audio_get_id(QEMUSoundCard *card);
|
||||
|
||||
#define DEFINE_AUDIO_PROPERTIES(_s, _f) \
|
||||
DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
|
||||
|
||||
#endif /* QEMU_AUDIO_H */
|
||||
+40
-71
@@ -40,74 +40,66 @@ struct audio_callback {
|
||||
|
||||
struct audio_pcm_info {
|
||||
int bits;
|
||||
bool is_signed;
|
||||
bool is_float;
|
||||
int sign;
|
||||
int freq;
|
||||
int nchannels;
|
||||
int bytes_per_frame;
|
||||
int align;
|
||||
int shift;
|
||||
int bytes_per_second;
|
||||
int swap_endianness;
|
||||
};
|
||||
|
||||
typedef struct AudioState AudioState;
|
||||
typedef struct SWVoiceCap SWVoiceCap;
|
||||
|
||||
typedef struct STSampleBuffer {
|
||||
size_t pos, size;
|
||||
st_sample samples[];
|
||||
} STSampleBuffer;
|
||||
|
||||
typedef struct HWVoiceOut {
|
||||
AudioState *s;
|
||||
int enabled;
|
||||
int poll_mode;
|
||||
int pending_disable;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
f_sample *clip;
|
||||
|
||||
int rpos;
|
||||
uint64_t ts_helper;
|
||||
|
||||
STSampleBuffer *mix_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
struct st_sample *mix_buf;
|
||||
|
||||
size_t samples;
|
||||
int samples;
|
||||
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
|
||||
int ctl_caps;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
QLIST_ENTRY (HWVoiceOut) entries;
|
||||
} HWVoiceOut;
|
||||
|
||||
typedef struct HWVoiceIn {
|
||||
AudioState *s;
|
||||
int enabled;
|
||||
int poll_mode;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
t_sample *conv;
|
||||
|
||||
size_t total_samples_captured;
|
||||
int wpos;
|
||||
int total_samples_captured;
|
||||
uint64_t ts_helper;
|
||||
|
||||
STSampleBuffer *conv_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
struct st_sample *conv_buf;
|
||||
|
||||
size_t samples;
|
||||
int samples;
|
||||
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
|
||||
int ctl_caps;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
QLIST_ENTRY (HWVoiceIn) entries;
|
||||
} HWVoiceIn;
|
||||
|
||||
struct SWVoiceOut {
|
||||
QEMUSoundCard *card;
|
||||
AudioState *s;
|
||||
struct audio_pcm_info info;
|
||||
t_sample *conv;
|
||||
int64_t ratio;
|
||||
struct st_sample *buf;
|
||||
void *rate;
|
||||
size_t total_hw_samples_mixed;
|
||||
int total_hw_samples_mixed;
|
||||
int active;
|
||||
int empty;
|
||||
HWVoiceOut *hw;
|
||||
@@ -119,12 +111,11 @@ struct SWVoiceOut {
|
||||
|
||||
struct SWVoiceIn {
|
||||
QEMUSoundCard *card;
|
||||
AudioState *s;
|
||||
int active;
|
||||
struct audio_pcm_info info;
|
||||
int64_t ratio;
|
||||
void *rate;
|
||||
size_t total_hw_samples_acquired;
|
||||
int total_hw_samples_acquired;
|
||||
struct st_sample *buf;
|
||||
f_sample *clip;
|
||||
HWVoiceIn *hw;
|
||||
@@ -146,46 +137,24 @@ struct audio_driver {
|
||||
int max_voices_in;
|
||||
int voice_size_out;
|
||||
int voice_size_in;
|
||||
int ctl_caps;
|
||||
QLIST_ENTRY(audio_driver) next;
|
||||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||
void (*run_buffer_out)(HWVoiceOut *hw);
|
||||
/*
|
||||
* get a buffer that after later can be passed to put_buffer_out; optional
|
||||
* returns the buffer, and writes it's size to size (in bytes)
|
||||
* this is unrelated to the above buffer_size_out function
|
||||
*/
|
||||
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
|
||||
/*
|
||||
* put back the buffer returned by get_buffer_out; optional
|
||||
* buf must be equal the pointer returned by get_buffer_out,
|
||||
* size may be smaller
|
||||
*/
|
||||
size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
|
||||
void (*enable_out)(HWVoiceOut *hw, bool enable);
|
||||
void (*volume_out)(HWVoiceOut *hw, Volume *vol);
|
||||
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
int (*run_out) (HWVoiceOut *hw, int live);
|
||||
int (*write) (SWVoiceOut *sw, void *buf, int size);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
|
||||
int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
|
||||
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
|
||||
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void (*enable_in)(HWVoiceIn *hw, bool enable);
|
||||
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
|
||||
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
int (*run_in) (HWVoiceIn *hw);
|
||||
int (*read) (SWVoiceIn *sw, void *buf, int size);
|
||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
||||
};
|
||||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void audio_generic_run_buffer_out(HWVoiceOut *hw);
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
|
||||
|
||||
struct capture_callback {
|
||||
struct audio_capture_ops ops;
|
||||
void *opaque;
|
||||
@@ -219,11 +188,6 @@ typedef struct AudioState {
|
||||
int nb_hw_voices_in;
|
||||
int vm_running;
|
||||
int64_t period_ticks;
|
||||
|
||||
bool timer_running;
|
||||
uint64_t timer_last;
|
||||
|
||||
QTAILQ_ENTRY(AudioState) list;
|
||||
} AudioState;
|
||||
|
||||
extern const struct mixeng_volume nominal_volume;
|
||||
@@ -236,21 +200,26 @@ audio_driver *audio_driver_lookup(const char *name);
|
||||
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_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
|
||||
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
|
||||
|
||||
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
|
||||
|
||||
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
|
||||
int live, int pending);
|
||||
|
||||
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);
|
||||
void audio_run (const char *msg);
|
||||
|
||||
typedef struct RateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
} RateCtl;
|
||||
#define VOICE_ENABLE 1
|
||||
#define VOICE_DISABLE 2
|
||||
#define VOICE_VOLUME 3
|
||||
|
||||
void audio_rate_start(RateCtl *rate);
|
||||
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
|
||||
size_t bytes_avail);
|
||||
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
|
||||
|
||||
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
|
||||
static inline int audio_ring_dist (int dst, int src, int len)
|
||||
{
|
||||
return (dst >= src) ? (dst - src) : (len - src + dst);
|
||||
}
|
||||
|
||||
+5
-10
@@ -421,12 +421,11 @@ typedef struct {
|
||||
GList *path;
|
||||
} LegacyPrintVisitor;
|
||||
|
||||
static bool lv_start_struct(Visitor *v, const char *name, void **obj,
|
||||
static void lv_start_struct(Visitor *v, const char *name, void **obj,
|
||||
size_t size, Error **errp)
|
||||
{
|
||||
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||
lv->path = g_list_append(lv->path, g_strdup(name));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lv_end_struct(Visitor *v, void **obj)
|
||||
@@ -454,30 +453,27 @@ static void lv_print_key(Visitor *v, const char *name)
|
||||
printf("%s=", name);
|
||||
}
|
||||
|
||||
static bool lv_type_int64(Visitor *v, const char *name, int64_t *obj,
|
||||
static void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
lv_print_key(v, name);
|
||||
printf("%" PRIi64, *obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
||||
static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
lv_print_key(v, name);
|
||||
printf("%" PRIu64, *obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
|
||||
static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
|
||||
{
|
||||
lv_print_key(v, name);
|
||||
printf("%s", *obj ? "on" : "off");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
|
||||
static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
|
||||
{
|
||||
const char *str = *obj;
|
||||
lv_print_key(v, name);
|
||||
@@ -488,7 +484,6 @@ static bool lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
|
||||
}
|
||||
putchar(*str++);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lv_complete(Visitor *v, void *opaque)
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "audio-pt"
|
||||
|
||||
#include "audio_int.h"
|
||||
#include "audio_pt_int.h"
|
||||
|
||||
static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (pt->drv, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (NULL, "\n");
|
||||
AUD_log (pt->drv, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
|
||||
void *opaque, const char *drv, const char *cap)
|
||||
{
|
||||
int err, err2;
|
||||
const char *efunc;
|
||||
sigset_t set, old_set;
|
||||
|
||||
p->drv = drv;
|
||||
|
||||
err = sigfillset (&set);
|
||||
if (err) {
|
||||
logerr(p, errno, "%s(%s): sigfillset failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = pthread_mutex_init (&p->mutex, NULL);
|
||||
if (err) {
|
||||
efunc = "pthread_mutex_init";
|
||||
goto err0;
|
||||
}
|
||||
|
||||
err = pthread_cond_init (&p->cond, NULL);
|
||||
if (err) {
|
||||
efunc = "pthread_cond_init";
|
||||
goto err1;
|
||||
}
|
||||
|
||||
err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
|
||||
if (err) {
|
||||
efunc = "pthread_sigmask";
|
||||
goto err2;
|
||||
}
|
||||
|
||||
err = pthread_create (&p->thread, NULL, func, opaque);
|
||||
|
||||
err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
|
||||
if (err2) {
|
||||
logerr(p, err2, "%s(%s): pthread_sigmask (restore) failed",
|
||||
cap, __func__);
|
||||
/* We have failed to restore original signal mask, all bets are off,
|
||||
so terminate the process */
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
efunc = "pthread_create";
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
err2 = pthread_cond_destroy (&p->cond);
|
||||
if (err2) {
|
||||
logerr(p, err2, "%s(%s): pthread_cond_destroy failed", cap, __func__);
|
||||
}
|
||||
|
||||
err1:
|
||||
err2 = pthread_mutex_destroy (&p->mutex);
|
||||
if (err2) {
|
||||
logerr(p, err2, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
|
||||
}
|
||||
|
||||
err0:
|
||||
logerr(p, err, "%s(%s): %s failed", cap, __func__, efunc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int audio_pt_fini (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err, ret = 0;
|
||||
|
||||
err = pthread_cond_destroy (&p->cond);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_cond_destroy failed", cap, __func__);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
err = pthread_mutex_destroy (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int audio_pt_lock (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_lock (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_lock failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_unlock (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_wait (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_cond_wait (&p->cond, &p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_cond_wait failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
err = pthread_cond_signal (&p->cond);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_cond_signal failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
|
||||
{
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
err = pthread_join (p->thread, &ret);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_join failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
*arg = ret;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef QEMU_AUDIO_PT_INT_H
|
||||
#define QEMU_AUDIO_PT_INT_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct audio_pt {
|
||||
const char *drv;
|
||||
pthread_t thread;
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
|
||||
const char *, const char *);
|
||||
int audio_pt_fini (struct audio_pt *, const char *);
|
||||
int audio_pt_lock (struct audio_pt *, const char *);
|
||||
int audio_pt_unlock (struct audio_pt *, const char *);
|
||||
int audio_pt_wait (struct audio_pt *, const char *);
|
||||
int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
|
||||
int audio_pt_join (struct audio_pt *, void **, const char *);
|
||||
|
||||
#endif /* QEMU_AUDIO_PT_INT_H */
|
||||
+54
-78
@@ -36,9 +36,9 @@
|
||||
#define HWBUF hw->conv_buf
|
||||
#endif
|
||||
|
||||
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
||||
struct audio_driver *drv)
|
||||
static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
int max_voices = glue (drv->max_voices_, TYPE);
|
||||
int voice_size = glue (drv->voice_size_, TYPE);
|
||||
|
||||
@@ -71,24 +71,20 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
||||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
g_free(hw->buf_emul);
|
||||
g_free (HWBUF);
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
||||
static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
|
||||
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) {
|
||||
size_t samples = hw->samples;
|
||||
if (audio_bug(__func__, samples == 0)) {
|
||||
dolog("Attempted to allocate empty buffer\n");
|
||||
}
|
||||
|
||||
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
|
||||
HWBUF->size = samples;
|
||||
} else {
|
||||
HWBUF = NULL;
|
||||
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
|
||||
if (!HWBUF) {
|
||||
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
|
||||
hw->samples);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
|
||||
@@ -107,11 +103,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
int samples;
|
||||
|
||||
if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
|
||||
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
|
||||
|
||||
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
|
||||
if (!sw->buf) {
|
||||
@@ -153,23 +145,15 @@ static int glue (audio_pcm_sw_init_, TYPE) (
|
||||
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
|
||||
#endif
|
||||
|
||||
if (sw->info.is_float) {
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv_float[sw->info.nchannels == 2];
|
||||
sw->conv = mixeng_conv
|
||||
#else
|
||||
sw->clip = mixeng_clip_float[sw->info.nchannels == 2];
|
||||
sw->clip = mixeng_clip
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv
|
||||
#else
|
||||
sw->clip = mixeng_clip
|
||||
#endif
|
||||
[sw->info.nchannels == 2]
|
||||
[sw->info.is_signed]
|
||||
[sw->info.swap_endianness]
|
||||
[audio_bits_to_index(sw->info.bits)];
|
||||
}
|
||||
[sw->info.nchannels == 2]
|
||||
[sw->info.sign]
|
||||
[sw->info.swap_endianness]
|
||||
[audio_bits_to_index (sw->info.bits)];
|
||||
|
||||
sw->name = g_strdup (name);
|
||||
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
|
||||
@@ -199,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
|
||||
|
||||
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
HW *hw = *hwp;
|
||||
AudioState *s = hw->s;
|
||||
|
||||
if (!hw->sw_head.lh_first) {
|
||||
#ifdef DAC
|
||||
@@ -215,14 +199,15 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
|
||||
}
|
||||
}
|
||||
|
||||
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
|
||||
static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
|
||||
}
|
||||
|
||||
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
|
||||
static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
|
||||
{
|
||||
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
|
||||
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
|
||||
if (hw->enabled) {
|
||||
return hw;
|
||||
}
|
||||
@@ -230,10 +215,12 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
|
||||
struct audsettings *as)
|
||||
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
|
||||
HW *hw,
|
||||
struct audsettings *as
|
||||
)
|
||||
{
|
||||
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
|
||||
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
|
||||
if (audio_pcm_info_eq (&hw->info, as)) {
|
||||
return hw;
|
||||
}
|
||||
@@ -241,10 +228,10 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
struct audsettings *as)
|
||||
static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
{
|
||||
HW *hw;
|
||||
AudioState *s = &glob_audio_state;
|
||||
struct audio_driver *drv = s->drv;
|
||||
|
||||
if (!glue (s->nb_hw_voices_, TYPE)) {
|
||||
@@ -268,8 +255,8 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw->s = s;
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
hw->ctl_caps = drv->ctl_caps;
|
||||
|
||||
QLIST_INIT (&hw->sw_head);
|
||||
#ifdef DAC
|
||||
@@ -280,29 +267,23 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, hw->samples <= 0)) {
|
||||
dolog("hw->samples=%zd\n", hw->samples);
|
||||
dolog ("hw->samples=%d\n", hw->samples);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (hw->info.is_float) {
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
|
||||
hw->clip = mixeng_clip
|
||||
#else
|
||||
hw->conv = mixeng_conv_float[hw->info.nchannels == 2];
|
||||
hw->conv = mixeng_conv
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip
|
||||
#else
|
||||
hw->conv = mixeng_conv
|
||||
#endif
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.is_signed]
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index(hw->info.bits)];
|
||||
}
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.sign]
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index (hw->info.bits)];
|
||||
|
||||
glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
|
||||
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
|
||||
goto err1;
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
|
||||
glue (s->nb_hw_voices_, TYPE) -= 1;
|
||||
@@ -330,8 +311,6 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
dev->u.coreaudio.TYPE);
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
return dev->u.dsound.TYPE;
|
||||
case AUDIODEV_DRIVER_JACK:
|
||||
return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE);
|
||||
case AUDIODEV_DRIVER_OSS:
|
||||
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
|
||||
case AUDIODEV_DRIVER_PA:
|
||||
@@ -349,33 +328,33 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
abort();
|
||||
}
|
||||
|
||||
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
|
||||
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
|
||||
{
|
||||
HW *hw;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (!pdo->mixing_engine || pdo->fixed_settings) {
|
||||
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
|
||||
if (!pdo->mixing_engine || hw) {
|
||||
if (pdo->fixed_settings) {
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
|
||||
hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
|
||||
hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
|
||||
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
|
||||
return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
|
||||
return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
|
||||
}
|
||||
|
||||
static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
|
||||
AudioState *s,
|
||||
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||
const char *sw_name,
|
||||
struct audsettings *as
|
||||
)
|
||||
@@ -383,6 +362,7 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
|
||||
SW *sw;
|
||||
HW *hw;
|
||||
struct audsettings hw_as;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (pdo->fixed_settings) {
|
||||
@@ -398,9 +378,8 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
|
||||
sw_name ? sw_name : "unknown", sizeof (*sw));
|
||||
goto err1;
|
||||
}
|
||||
sw->s = s;
|
||||
|
||||
hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
|
||||
hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
|
||||
if (!hw) {
|
||||
goto err2;
|
||||
}
|
||||
@@ -451,8 +430,8 @@ SW *glue (AUD_open_, TYPE) (
|
||||
struct audsettings *as
|
||||
)
|
||||
{
|
||||
AudioState *s;
|
||||
AudiodevPerDirectionOptions *pdo;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||
@@ -460,9 +439,6 @@ SW *glue (AUD_open_, TYPE) (
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s = card->state;
|
||||
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
||||
name, as->freq, as->nchannels, as->fmt);
|
||||
|
||||
@@ -500,7 +476,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||
}
|
||||
}
|
||||
else {
|
||||
sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
|
||||
sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
|
||||
if (!sw) {
|
||||
dolog ("Failed to create voice `%s'\n", name);
|
||||
return NULL;
|
||||
|
||||
+75
-56
@@ -43,6 +43,9 @@ typedef struct coreaudioVoiceOut {
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
AudioDeviceIOProcID ioprocid;
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
} coreaudioVoiceOut;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
@@ -394,29 +397,31 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
|
||||
static ret_type glue(coreaudio_, name)args_decl \
|
||||
{ \
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
|
||||
ret_type ret; \
|
||||
\
|
||||
if (coreaudio_lock(core, "coreaudio_" #name)) { \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
ret = glue(audio_generic_, name)args; \
|
||||
\
|
||||
coreaudio_unlock(core, "coreaudio_" #name); \
|
||||
return ret; \
|
||||
static int coreaudio_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
int decr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (coreaudio_lock (core, "coreaudio_run_out")) {
|
||||
return 0;
|
||||
}
|
||||
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size))
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
#undef COREAUDIO_WRAPPER_FUNC
|
||||
|
||||
if (core->decr > live) {
|
||||
ldebug ("core->decr %d live %d core->live %d\n",
|
||||
core->decr,
|
||||
live,
|
||||
core->live);
|
||||
}
|
||||
|
||||
decr = audio_MIN (core->decr, live);
|
||||
core->decr -= decr;
|
||||
|
||||
core->live = live - decr;
|
||||
hw->rpos = core->rpos;
|
||||
|
||||
coreaudio_unlock (core, "coreaudio_run_out");
|
||||
return decr;
|
||||
}
|
||||
|
||||
/* callback to feed audiooutput buffer */
|
||||
static OSStatus audioDeviceIOProc(
|
||||
@@ -428,11 +433,19 @@ static OSStatus audioDeviceIOProc(
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* hwptr)
|
||||
{
|
||||
UInt32 frameCount, pending_frames;
|
||||
void *out = outOutputData->mBuffers[0].mData;
|
||||
UInt32 frame, frameCount;
|
||||
float *out = outOutputData->mBuffers[0].mData;
|
||||
HWVoiceOut *hw = hwptr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
|
||||
size_t len;
|
||||
int rpos, live;
|
||||
struct st_sample *src;
|
||||
#ifndef FLOAT_MIXENG
|
||||
#ifdef RECIPROCAL
|
||||
const float scale = 1.f / UINT_MAX;
|
||||
#else
|
||||
const float scale = UINT_MAX;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (coreaudio_lock (core, "audioDeviceIOProc")) {
|
||||
inInputTime = 0;
|
||||
@@ -440,37 +453,47 @@ static OSStatus audioDeviceIOProc(
|
||||
}
|
||||
|
||||
frameCount = core->audioDevicePropertyBufferFrameSize;
|
||||
pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
|
||||
live = core->live;
|
||||
|
||||
/* if there are not enough samples, set signal and return */
|
||||
if (pending_frames < frameCount) {
|
||||
if (live < frameCount) {
|
||||
inInputTime = 0;
|
||||
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = frameCount * hw->info.bytes_per_frame;
|
||||
while (len) {
|
||||
size_t write_len;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
rpos = core->rpos;
|
||||
src = hw->mix_buf + rpos;
|
||||
|
||||
write_len = MIN(MIN(hw->pending_emul, len),
|
||||
hw->size_emul - start);
|
||||
|
||||
memcpy(out, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= write_len;
|
||||
len -= write_len;
|
||||
out += write_len;
|
||||
/* fill buffer */
|
||||
for (frame = 0; frame < frameCount; frame++) {
|
||||
#ifdef FLOAT_MIXENG
|
||||
*out++ = src[frame].l; /* left channel */
|
||||
*out++ = src[frame].r; /* right channel */
|
||||
#else
|
||||
#ifdef RECIPROCAL
|
||||
*out++ = src[frame].l * scale; /* left channel */
|
||||
*out++ = src[frame].r * scale; /* right channel */
|
||||
#else
|
||||
*out++ = src[frame].l / scale; /* left channel */
|
||||
*out++ = src[frame].r / scale; /* right channel */
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
rpos = (rpos + frameCount) % hw->samples;
|
||||
core->decr += frameCount;
|
||||
core->rpos = rpos;
|
||||
|
||||
coreaudio_unlock (core, "audioDeviceIOProc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
@@ -482,7 +505,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
Audiodev *dev = drv_opaque;
|
||||
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
|
||||
int frames;
|
||||
struct audsettings fake_as;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
@@ -491,9 +513,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
return -1;
|
||||
}
|
||||
|
||||
fake_as = *as;
|
||||
as = &fake_as;
|
||||
as->fmt = AUDIO_FORMAT_F32;
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
status = coreaudio_get_voice(&core->outputDeviceID);
|
||||
@@ -562,7 +581,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
|
||||
status = coreaudio_set_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
@@ -629,12 +647,13 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
}
|
||||
}
|
||||
|
||||
static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (enable) {
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
/* start playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
|
||||
@@ -642,7 +661,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
|
||||
coreaudio_logerr (status, "Could not resume playback\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
/* stop playback */
|
||||
if (!audio_is_cleaning_up()) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
@@ -653,7 +674,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *coreaudio_audio_init(Audiodev *dev)
|
||||
@@ -668,13 +691,9 @@ static void coreaudio_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
.init_out = coreaudio_init_out,
|
||||
.fini_out = coreaudio_fini_out,
|
||||
/* wrapper for audio_generic_write */
|
||||
.run_out = coreaudio_run_out,
|
||||
.write = coreaudio_write,
|
||||
/* wrapper for audio_generic_get_buffer_out */
|
||||
.get_buffer_out = coreaudio_get_buffer_out,
|
||||
/* wrapper for audio_generic_put_buffer_out */
|
||||
.put_buffer_out = coreaudio_put_buffer_out,
|
||||
.enable_out = coreaudio_enable_out
|
||||
.ctl_out = coreaudio_ctl_out
|
||||
};
|
||||
|
||||
static struct audio_driver coreaudio_audio_driver = {
|
||||
|
||||
+26
-28
@@ -29,8 +29,6 @@
|
||||
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
|
||||
#define FIELD dsound_capture_buffer
|
||||
#define FIELD2 dsound_capture
|
||||
#define HWVOICE HWVoiceIn
|
||||
#define DSOUNDVOICE DSoundVoiceIn
|
||||
#else
|
||||
#define NAME "playback buffer"
|
||||
#define NAME2 "DirectSound"
|
||||
@@ -39,8 +37,6 @@
|
||||
#define BUFPTR LPDIRECTSOUNDBUFFER
|
||||
#define FIELD dsound_buffer
|
||||
#define FIELD2 dsound
|
||||
#define HWVOICE HWVoiceOut
|
||||
#define DSOUNDVOICE DSoundVoiceOut
|
||||
#endif
|
||||
|
||||
static int glue (dsound_unlock_, TYPE) (
|
||||
@@ -76,6 +72,8 @@ static int glue (dsound_lock_, TYPE) (
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
LPVOID p1 = NULL, p2 = NULL;
|
||||
DWORD blen1 = 0, blen2 = 0;
|
||||
DWORD flag;
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
@@ -83,7 +81,7 @@ static int glue (dsound_lock_, TYPE) (
|
||||
#else
|
||||
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
|
||||
#endif
|
||||
hr = glue(IFACE, _Lock)(buf, pos, len, p1p, blen1p, p2p, blen2p, flag);
|
||||
hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
|
||||
|
||||
if (FAILED (hr)) {
|
||||
#ifndef DSBTYPE_IN
|
||||
@@ -98,34 +96,34 @@ static int glue (dsound_lock_, TYPE) (
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((p1p && *p1p && (*blen1p % info->bytes_per_frame)) ||
|
||||
(p2p && *p2p && (*blen2p % info->bytes_per_frame))) {
|
||||
dolog("DirectSound returned misaligned buffer %ld %ld\n",
|
||||
*blen1p, *blen2p);
|
||||
glue(dsound_unlock_, TYPE)(buf, *p1p, p2p ? *p2p : NULL, *blen1p,
|
||||
blen2p ? *blen2p : 0);
|
||||
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
|
||||
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
|
||||
blen1, blen2);
|
||||
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (p1p && !*p1p && *blen1p) {
|
||||
dolog("warning: !p1 && blen1=%ld\n", *blen1p);
|
||||
*blen1p = 0;
|
||||
if (!p1 && blen1) {
|
||||
dolog ("warning: !p1 && blen1=%ld\n", blen1);
|
||||
blen1 = 0;
|
||||
}
|
||||
|
||||
if (p2p && !*p2p && *blen2p) {
|
||||
dolog("warning: !p2 && blen2=%ld\n", *blen2p);
|
||||
*blen2p = 0;
|
||||
if (!p2 && blen2) {
|
||||
dolog ("warning: !p2 && blen2=%ld\n", blen2);
|
||||
blen2 = 0;
|
||||
}
|
||||
|
||||
*p1p = p1;
|
||||
*p2p = p2;
|
||||
*blen1p = blen1;
|
||||
*blen2p = blen2;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*p1p = NULL - 1;
|
||||
*p2p = NULL - 1;
|
||||
*blen1p = -1;
|
||||
if (p2p) {
|
||||
*p2p = NULL - 1;
|
||||
*blen2p = -1;
|
||||
}
|
||||
*blen2p = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -244,23 +242,25 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
ds->first_time = true;
|
||||
ds->first_time = 1;
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
|
||||
if (bc.dwBufferBytes % hw->info.bytes_per_frame) {
|
||||
if (bc.dwBufferBytes & hw->info.align) {
|
||||
dolog (
|
||||
"GetCaps returned misaligned buffer size %ld, alignment %d\n",
|
||||
bc.dwBufferBytes, hw->info.bytes_per_frame
|
||||
bc.dwBufferBytes, hw->info.align + 1
|
||||
);
|
||||
}
|
||||
hw->size_emul = bc.dwBufferBytes;
|
||||
hw->samples = bc.dwBufferBytes / hw->info.bytes_per_frame;
|
||||
hw->samples = bc.dwBufferBytes >> hw->info.shift;
|
||||
ds->s = s;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("caps %ld, desc %ld\n",
|
||||
bc.dwBufferBytes, bd.dwBufferBytes);
|
||||
|
||||
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
|
||||
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@@ -276,5 +276,3 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
#undef BUFPTR
|
||||
#undef FIELD
|
||||
#undef FIELD2
|
||||
#undef HWVOICE
|
||||
#undef DSOUNDVOICE
|
||||
+286
-118
@@ -53,14 +53,20 @@ typedef struct {
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
LPDIRECTSOUNDBUFFER dsound_buffer;
|
||||
bool first_time;
|
||||
DWORD old_pos;
|
||||
int first_time;
|
||||
dsound *s;
|
||||
#ifdef DEBUG_DSOUND
|
||||
DWORD old_ppos;
|
||||
DWORD played;
|
||||
DWORD mixed;
|
||||
#endif
|
||||
} DSoundVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceIn hw;
|
||||
int first_time;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
|
||||
bool first_time;
|
||||
dsound *s;
|
||||
} DSoundVoiceIn;
|
||||
|
||||
@@ -237,6 +243,11 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
|
||||
dsound_log_hresult (hr);
|
||||
}
|
||||
|
||||
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
|
||||
{
|
||||
return muldiv64(usecs, info->bytes_per_second, 1000000);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
static void print_wave_format (WAVEFORMATEX *wfx)
|
||||
{
|
||||
@@ -279,7 +290,7 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*statusp & DSBSTATUS_BUFFERLOST) {
|
||||
if (*statusp & DSERR_BUFFERLOST) {
|
||||
dsound_restore_out(dsb, s);
|
||||
return -1;
|
||||
}
|
||||
@@ -301,6 +312,33 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len;
|
||||
int src_len2 = 0;
|
||||
int pos = hw->rpos + dst_len;
|
||||
struct st_sample *src1 = hw->mix_buf + hw->rpos;
|
||||
struct st_sample *src2 = NULL;
|
||||
|
||||
if (pos > hw->samples) {
|
||||
src_len1 = hw->samples - hw->rpos;
|
||||
src2 = hw->mix_buf;
|
||||
src_len2 = dst_len - src_len1;
|
||||
pos = src_len2;
|
||||
}
|
||||
|
||||
if (src_len1) {
|
||||
hw->clip (dst, src1, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
dst = advance (dst, src_len1 << hw->info.shift);
|
||||
hw->clip (dst, src2, src_len2);
|
||||
}
|
||||
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||
dsound *s)
|
||||
{
|
||||
@@ -312,7 +350,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||
dsb,
|
||||
&hw->info,
|
||||
0,
|
||||
hw->size_emul,
|
||||
hw->samples << hw->info.shift,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2,
|
||||
1,
|
||||
@@ -322,8 +360,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||
return;
|
||||
}
|
||||
|
||||
len1 = blen1 / hw->info.bytes_per_frame;
|
||||
len2 = blen2 / hw->info.bytes_per_frame;
|
||||
len1 = blen1 >> hw->info.shift;
|
||||
len2 = blen2 >> hw->info.shift;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
|
||||
@@ -363,7 +401,7 @@ static int dsound_open (dsound *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD status;
|
||||
@@ -373,17 +411,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
||||
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to control voice without a buffer\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (dsound_get_status_out (dsb, &status, s)) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status & DSBSTATUS_PLAYING) {
|
||||
dolog ("warning: Voice is already playing\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dsound_clear_sample (hw, dsb, s);
|
||||
@@ -391,85 +430,170 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
||||
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not start playing buffer\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (dsound_get_status_out (dsb, &status, s)) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status & DSBSTATUS_PLAYING) {
|
||||
hr = IDirectSoundBuffer_Stop (dsb);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not stop playing buffer\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Voice is not playing\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
static int dsound_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
HRESULT hr;
|
||||
DWORD ppos, wpos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition(
|
||||
dsb, &ppos, ds->first_time ? &wpos : NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get playback buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ds->first_time) {
|
||||
hw->pos_emul = wpos;
|
||||
ds->first_time = false;
|
||||
}
|
||||
|
||||
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
||||
|
||||
if (req_size == 0) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||
&act_size, NULL, false, ds->s);
|
||||
if (err) {
|
||||
dolog("Failed to lock buffer\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*size = act_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
HRESULT hr;
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
|
||||
int len, hwshift;
|
||||
DWORD blen1, blen2;
|
||||
DWORD len1, len2;
|
||||
DWORD decr;
|
||||
DWORD wpos, ppos, old_pos;
|
||||
LPVOID p1, p2;
|
||||
int bufsize;
|
||||
dsound *s = ds->s;
|
||||
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
|
||||
|
||||
if (err) {
|
||||
dolog("Failed to unlock buffer!!\n");
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to run empty with playback buffer\n");
|
||||
return 0;
|
||||
}
|
||||
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
|
||||
|
||||
return len;
|
||||
hwshift = hw->info.shift;
|
||||
bufsize = hw->samples << hwshift;
|
||||
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition (
|
||||
dsb,
|
||||
&ppos,
|
||||
ds->first_time ? &wpos : NULL
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get playback buffer position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = live << hwshift;
|
||||
|
||||
if (ds->first_time) {
|
||||
if (dso->latency) {
|
||||
DWORD cur_blat;
|
||||
|
||||
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
||||
ds->first_time = 0;
|
||||
old_pos = wpos;
|
||||
old_pos +=
|
||||
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
|
||||
old_pos %= bufsize;
|
||||
old_pos &= ~hw->info.align;
|
||||
}
|
||||
else {
|
||||
old_pos = wpos;
|
||||
}
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->played = 0;
|
||||
ds->mixed = 0;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (ds->old_pos == ppos) {
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("old_pos == ppos\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
|
||||
#endif
|
||||
old_pos = ds->old_pos;
|
||||
}
|
||||
|
||||
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
||||
len = ppos - old_pos;
|
||||
}
|
||||
else {
|
||||
if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
|
||||
len = bufsize - old_pos + ppos;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, len < 0 || len > bufsize)) {
|
||||
dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
|
||||
len, bufsize, old_pos, ppos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
len &= ~hw->info.align;
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->old_ppos = ppos;
|
||||
#endif
|
||||
err = dsound_lock_out (
|
||||
dsb,
|
||||
&hw->info,
|
||||
old_pos,
|
||||
len,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2,
|
||||
0,
|
||||
s
|
||||
);
|
||||
if (err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && len1) {
|
||||
dsound_write_sample (hw, p1, len1);
|
||||
}
|
||||
|
||||
if (p2 && len2) {
|
||||
dsound_write_sample (hw, p2, len2);
|
||||
}
|
||||
|
||||
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
|
||||
ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->mixed += decr << hwshift;
|
||||
|
||||
dolog ("played %lu mixed %lu diff %ld sec %f\n",
|
||||
ds->played,
|
||||
ds->mixed,
|
||||
ds->mixed - ds->played,
|
||||
abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
|
||||
#endif
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void dsound_enable_in(HWVoiceIn *hw, bool enable)
|
||||
static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD status;
|
||||
@@ -478,17 +602,18 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
|
||||
|
||||
if (!dscb) {
|
||||
dolog ("Attempt to control capture voice without a buffer\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (dsound_get_status_in (dscb, &status)) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status & DSCBSTATUS_CAPTURING) {
|
||||
dolog ("warning: Voice is already capturing\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* clear ?? */
|
||||
@@ -496,80 +621,125 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
|
||||
hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not start capturing\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (dsound_get_status_in (dscb, &status)) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status & DSCBSTATUS_CAPTURING) {
|
||||
hr = IDirectSoundCaptureBuffer_Stop (dscb);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not stop capturing\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Voice is not capturing\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
||||
static int dsound_read (SWVoiceIn *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, len);
|
||||
}
|
||||
|
||||
static int dsound_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
|
||||
HRESULT hr;
|
||||
DWORD cpos, rpos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
int live, len, dead;
|
||||
DWORD blen1, blen2;
|
||||
DWORD len1, len2;
|
||||
DWORD decr;
|
||||
DWORD cpos, rpos;
|
||||
LPVOID p1, p2;
|
||||
int hwshift;
|
||||
dsound *s = ds->s;
|
||||
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
|
||||
dscb, &cpos, ds->first_time ? &rpos : NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get capture buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
if (!dscb) {
|
||||
dolog ("Attempt to run without capture buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hwshift = hw->info.shift;
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
|
||||
dscb,
|
||||
&cpos,
|
||||
ds->first_time ? &rpos : NULL
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get capture buffer position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ds->first_time) {
|
||||
hw->pos_emul = rpos;
|
||||
ds->first_time = false;
|
||||
ds->first_time = 0;
|
||||
if (rpos & hw->info.align) {
|
||||
ldebug ("warning: Misaligned capture read position %ld(%d)\n",
|
||||
rpos, hw->info.align);
|
||||
}
|
||||
hw->wpos = rpos >> hwshift;
|
||||
}
|
||||
|
||||
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
|
||||
|
||||
if (req_size == 0) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
if (cpos & hw->info.align) {
|
||||
ldebug ("warning: Misaligned capture position %ld(%d)\n",
|
||||
cpos, hw->info.align);
|
||||
}
|
||||
cpos >>= hwshift;
|
||||
|
||||
err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||
&act_size, NULL, false, ds->s);
|
||||
len = audio_ring_dist (cpos, hw->wpos, hw->samples);
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
len = audio_MIN (len, dead);
|
||||
|
||||
err = dsound_lock_in (
|
||||
dscb,
|
||||
&hw->info,
|
||||
hw->wpos << hwshift,
|
||||
len << hwshift,
|
||||
&p1,
|
||||
&p2,
|
||||
&blen1,
|
||||
&blen2,
|
||||
0,
|
||||
s
|
||||
);
|
||||
if (err) {
|
||||
dolog("Failed to lock buffer\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*size = act_size;
|
||||
return ret;
|
||||
}
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
|
||||
static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
|
||||
{
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
|
||||
int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
|
||||
|
||||
if (err) {
|
||||
dolog("Failed to unlock buffer!!\n");
|
||||
return;
|
||||
if (p1 && len1) {
|
||||
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
|
||||
}
|
||||
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
|
||||
|
||||
if (p2 && len2) {
|
||||
hw->conv (hw->conv_buf, p2, len2);
|
||||
}
|
||||
|
||||
dsound_unlock_in (dscb, p1, p2, blen1, blen2);
|
||||
hw->wpos = (hw->wpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void dsound_audio_fini (void *opaque)
|
||||
@@ -685,17 +855,15 @@ static void *dsound_audio_init(Audiodev *dev)
|
||||
static struct audio_pcm_ops dsound_pcm_ops = {
|
||||
.init_out = dsound_init_out,
|
||||
.fini_out = dsound_fini_out,
|
||||
.write = audio_generic_write,
|
||||
.get_buffer_out = dsound_get_buffer_out,
|
||||
.put_buffer_out = dsound_put_buffer_out,
|
||||
.enable_out = dsound_enable_out,
|
||||
.run_out = dsound_run_out,
|
||||
.write = dsound_write,
|
||||
.ctl_out = dsound_ctl_out,
|
||||
|
||||
.init_in = dsound_init_in,
|
||||
.fini_in = dsound_fini_in,
|
||||
.read = audio_generic_read,
|
||||
.get_buffer_in = dsound_get_buffer_in,
|
||||
.put_buffer_in = dsound_put_buffer_in,
|
||||
.enable_in = dsound_enable_in,
|
||||
.run_in = dsound_run_in,
|
||||
.read = dsound_read,
|
||||
.ctl_in = dsound_ctl_in
|
||||
};
|
||||
|
||||
static struct audio_driver dsound_audio_driver = {
|
||||
|
||||
@@ -1,670 +0,0 @@
|
||||
/*
|
||||
* QEMU JACK Audio Connection Kit Client
|
||||
*
|
||||
* Copyright (c) 2020 Geoffrey McRae (gnif)
|
||||
*
|
||||
* 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/module.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "jack"
|
||||
#include "audio_int.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/thread.h>
|
||||
|
||||
struct QJack;
|
||||
|
||||
typedef enum QJackState {
|
||||
QJACK_STATE_DISCONNECTED,
|
||||
QJACK_STATE_RUNNING,
|
||||
QJACK_STATE_SHUTDOWN
|
||||
}
|
||||
QJackState;
|
||||
|
||||
typedef struct QJackBuffer {
|
||||
int channels;
|
||||
int frames;
|
||||
uint32_t used;
|
||||
int rptr, wptr;
|
||||
float **data;
|
||||
}
|
||||
QJackBuffer;
|
||||
|
||||
typedef struct QJackClient {
|
||||
AudiodevJackPerDirectionOptions *opt;
|
||||
|
||||
bool out;
|
||||
bool enabled;
|
||||
bool connect_ports;
|
||||
int packets;
|
||||
|
||||
QJackState state;
|
||||
jack_client_t *client;
|
||||
jack_nframes_t freq;
|
||||
|
||||
struct QJack *j;
|
||||
int nchannels;
|
||||
int buffersize;
|
||||
jack_port_t **port;
|
||||
QJackBuffer fifo;
|
||||
}
|
||||
QJackClient;
|
||||
|
||||
typedef struct QJackOut {
|
||||
HWVoiceOut hw;
|
||||
QJackClient c;
|
||||
}
|
||||
QJackOut;
|
||||
|
||||
typedef struct QJackIn {
|
||||
HWVoiceIn hw;
|
||||
QJackClient c;
|
||||
}
|
||||
QJackIn;
|
||||
|
||||
static int qjack_client_init(QJackClient *c);
|
||||
static void qjack_client_connect_ports(QJackClient *c);
|
||||
static void qjack_client_fini(QJackClient *c);
|
||||
|
||||
static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames)
|
||||
{
|
||||
buffer->channels = channels;
|
||||
buffer->frames = frames;
|
||||
buffer->used = 0;
|
||||
buffer->rptr = 0;
|
||||
buffer->wptr = 0;
|
||||
buffer->data = g_malloc(channels * sizeof(float *));
|
||||
for (int i = 0; i < channels; ++i) {
|
||||
buffer->data[i] = g_malloc(frames * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
static void qjack_buffer_clear(QJackBuffer *buffer)
|
||||
{
|
||||
assert(buffer->data);
|
||||
atomic_store_release(&buffer->used, 0);
|
||||
buffer->rptr = 0;
|
||||
buffer->wptr = 0;
|
||||
}
|
||||
|
||||
static void qjack_buffer_free(QJackBuffer *buffer)
|
||||
{
|
||||
if (!buffer->data) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer->channels; ++i) {
|
||||
g_free(buffer->data[i]);
|
||||
}
|
||||
|
||||
g_free(buffer->data);
|
||||
buffer->data = NULL;
|
||||
}
|
||||
|
||||
/* write PCM interleaved */
|
||||
static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size)
|
||||
{
|
||||
assert(buffer->data);
|
||||
const int samples = size / sizeof(float);
|
||||
int frames = samples / buffer->channels;
|
||||
const int avail = buffer->frames - atomic_load_acquire(&buffer->used);
|
||||
|
||||
if (frames > avail) {
|
||||
frames = avail;
|
||||
}
|
||||
|
||||
int copy = frames;
|
||||
int wptr = buffer->wptr;
|
||||
|
||||
while (copy) {
|
||||
|
||||
for (int c = 0; c < buffer->channels; ++c) {
|
||||
buffer->data[c][wptr] = *data++;
|
||||
}
|
||||
|
||||
if (++wptr == buffer->frames) {
|
||||
wptr = 0;
|
||||
}
|
||||
|
||||
--copy;
|
||||
}
|
||||
|
||||
buffer->wptr = wptr;
|
||||
|
||||
atomic_add(&buffer->used, frames);
|
||||
return frames * buffer->channels * sizeof(float);
|
||||
};
|
||||
|
||||
/* write PCM linear */
|
||||
static int qjack_buffer_write_l(QJackBuffer *buffer, float **dest, int frames)
|
||||
{
|
||||
assert(buffer->data);
|
||||
const int avail = buffer->frames - atomic_load_acquire(&buffer->used);
|
||||
int wptr = buffer->wptr;
|
||||
|
||||
if (frames > avail) {
|
||||
frames = avail;
|
||||
}
|
||||
|
||||
int right = buffer->frames - wptr;
|
||||
if (right > frames) {
|
||||
right = frames;
|
||||
}
|
||||
|
||||
const int left = frames - right;
|
||||
for (int c = 0; c < buffer->channels; ++c) {
|
||||
memcpy(buffer->data[c] + wptr, dest[c] , right * sizeof(float));
|
||||
memcpy(buffer->data[c] , dest[c] + right, left * sizeof(float));
|
||||
}
|
||||
|
||||
wptr += frames;
|
||||
if (wptr >= buffer->frames) {
|
||||
wptr -= buffer->frames;
|
||||
}
|
||||
buffer->wptr = wptr;
|
||||
|
||||
atomic_add(&buffer->used, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
/* read PCM interleaved */
|
||||
static int qjack_buffer_read(QJackBuffer *buffer, float *dest, int size)
|
||||
{
|
||||
assert(buffer->data);
|
||||
const int samples = size / sizeof(float);
|
||||
int frames = samples / buffer->channels;
|
||||
const int avail = atomic_load_acquire(&buffer->used);
|
||||
|
||||
if (frames > avail) {
|
||||
frames = avail;
|
||||
}
|
||||
|
||||
int copy = frames;
|
||||
int rptr = buffer->rptr;
|
||||
|
||||
while (copy) {
|
||||
|
||||
for (int c = 0; c < buffer->channels; ++c) {
|
||||
*dest++ = buffer->data[c][rptr];
|
||||
}
|
||||
|
||||
if (++rptr == buffer->frames) {
|
||||
rptr = 0;
|
||||
}
|
||||
|
||||
--copy;
|
||||
}
|
||||
|
||||
buffer->rptr = rptr;
|
||||
|
||||
atomic_sub(&buffer->used, frames);
|
||||
return frames * buffer->channels * sizeof(float);
|
||||
}
|
||||
|
||||
/* read PCM linear */
|
||||
static int qjack_buffer_read_l(QJackBuffer *buffer, float **dest, int frames)
|
||||
{
|
||||
assert(buffer->data);
|
||||
int copy = frames;
|
||||
const int used = atomic_load_acquire(&buffer->used);
|
||||
int rptr = buffer->rptr;
|
||||
|
||||
if (copy > used) {
|
||||
copy = used;
|
||||
}
|
||||
|
||||
int right = buffer->frames - rptr;
|
||||
if (right > copy) {
|
||||
right = copy;
|
||||
}
|
||||
|
||||
const int left = copy - right;
|
||||
for (int c = 0; c < buffer->channels; ++c) {
|
||||
memcpy(dest[c] , buffer->data[c] + rptr, right * sizeof(float));
|
||||
memcpy(dest[c] + right, buffer->data[c] , left * sizeof(float));
|
||||
}
|
||||
|
||||
rptr += copy;
|
||||
if (rptr >= buffer->frames) {
|
||||
rptr -= buffer->frames;
|
||||
}
|
||||
buffer->rptr = rptr;
|
||||
|
||||
atomic_sub(&buffer->used, copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
static int qjack_process(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
QJackClient *c = (QJackClient *)arg;
|
||||
|
||||
if (c->state != QJACK_STATE_RUNNING) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the buffers for the ports */
|
||||
float *buffers[c->nchannels];
|
||||
for (int i = 0; i < c->nchannels; ++i) {
|
||||
buffers[i] = jack_port_get_buffer(c->port[i], nframes);
|
||||
}
|
||||
|
||||
if (c->out) {
|
||||
if (likely(c->enabled)) {
|
||||
qjack_buffer_read_l(&c->fifo, buffers, nframes);
|
||||
} else {
|
||||
for(int i = 0; i < c->nchannels; ++i) {
|
||||
memset(buffers[i], 0, nframes * sizeof(float));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (likely(c->enabled)) {
|
||||
qjack_buffer_write_l(&c->fifo, buffers, nframes);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qjack_port_registration(jack_port_id_t port, int reg, void *arg)
|
||||
{
|
||||
if (reg) {
|
||||
QJackClient *c = (QJackClient *)arg;
|
||||
c->connect_ports = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int qjack_xrun(void *arg)
|
||||
{
|
||||
QJackClient *c = (QJackClient *)arg;
|
||||
if (c->state != QJACK_STATE_RUNNING) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qjack_buffer_clear(&c->fifo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qjack_shutdown(void *arg)
|
||||
{
|
||||
QJackClient *c = (QJackClient *)arg;
|
||||
c->state = QJACK_STATE_SHUTDOWN;
|
||||
}
|
||||
|
||||
static void qjack_client_recover(QJackClient *c)
|
||||
{
|
||||
if (c->state == QJACK_STATE_SHUTDOWN) {
|
||||
qjack_client_fini(c);
|
||||
}
|
||||
|
||||
/* packets is used simply to throttle this */
|
||||
if (c->state == QJACK_STATE_DISCONNECTED &&
|
||||
c->packets % 100 == 0) {
|
||||
|
||||
/* if enabled then attempt to recover */
|
||||
if (c->enabled) {
|
||||
dolog("attempting to reconnect to server\n");
|
||||
qjack_client_init(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t qjack_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
QJackOut *jo = (QJackOut *)hw;
|
||||
++jo->c.packets;
|
||||
|
||||
if (jo->c.state != QJACK_STATE_RUNNING) {
|
||||
qjack_client_recover(&jo->c);
|
||||
return len;
|
||||
}
|
||||
|
||||
qjack_client_connect_ports(&jo->c);
|
||||
return qjack_buffer_write(&jo->c.fifo, buf, len);
|
||||
}
|
||||
|
||||
static size_t qjack_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
{
|
||||
QJackIn *ji = (QJackIn *)hw;
|
||||
++ji->c.packets;
|
||||
|
||||
if (ji->c.state != QJACK_STATE_RUNNING) {
|
||||
qjack_client_recover(&ji->c);
|
||||
return len;
|
||||
}
|
||||
|
||||
qjack_client_connect_ports(&ji->c);
|
||||
return qjack_buffer_read(&ji->c.fifo, buf, len);
|
||||
}
|
||||
|
||||
static void qjack_client_connect_ports(QJackClient *c)
|
||||
{
|
||||
if (!c->connect_ports || !c->opt->connect_ports) {
|
||||
return;
|
||||
}
|
||||
|
||||
c->connect_ports = false;
|
||||
const char **ports;
|
||||
ports = jack_get_ports(c->client, c->opt->connect_ports, NULL,
|
||||
c->out ? JackPortIsInput : JackPortIsOutput);
|
||||
|
||||
if (!ports) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < c->nchannels && ports[i]; ++i) {
|
||||
const char *p = jack_port_name(c->port[i]);
|
||||
if (jack_port_connected_to(c->port[i], ports[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->out) {
|
||||
dolog("connect %s -> %s\n", p, ports[i]);
|
||||
jack_connect(c->client, p, ports[i]);
|
||||
} else {
|
||||
dolog("connect %s -> %s\n", ports[i], p);
|
||||
jack_connect(c->client, ports[i], p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qjack_client_init(QJackClient *c)
|
||||
{
|
||||
jack_status_t status;
|
||||
char client_name[jack_client_name_size()];
|
||||
jack_options_t options = JackNullOption;
|
||||
|
||||
if (c->state == QJACK_STATE_RUNNING) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->connect_ports = true;
|
||||
|
||||
snprintf(client_name, sizeof(client_name), "%s-%s",
|
||||
c->out ? "out" : "in",
|
||||
c->opt->client_name ? c->opt->client_name : qemu_get_vm_name());
|
||||
|
||||
if (c->opt->exact_name) {
|
||||
options |= JackUseExactName;
|
||||
}
|
||||
|
||||
if (!c->opt->start_server) {
|
||||
options |= JackNoStartServer;
|
||||
}
|
||||
|
||||
if (c->opt->server_name) {
|
||||
options |= JackServerName;
|
||||
}
|
||||
|
||||
c->client = jack_client_open(client_name, options, &status,
|
||||
c->opt->server_name);
|
||||
|
||||
if (c->client == NULL) {
|
||||
dolog("jack_client_open failed: status = 0x%2.0x\n", status);
|
||||
if (status & JackServerFailed) {
|
||||
dolog("unable to connect to JACK server\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->freq = jack_get_sample_rate(c->client);
|
||||
|
||||
if (status & JackServerStarted) {
|
||||
dolog("JACK server started\n");
|
||||
}
|
||||
|
||||
if (status & JackNameNotUnique) {
|
||||
dolog("JACK unique name assigned %s\n",
|
||||
jack_get_client_name(c->client));
|
||||
}
|
||||
|
||||
jack_set_process_callback(c->client, qjack_process , c);
|
||||
jack_set_port_registration_callback(c->client, qjack_port_registration, c);
|
||||
jack_set_xrun_callback(c->client, qjack_xrun, c);
|
||||
jack_on_shutdown(c->client, qjack_shutdown, c);
|
||||
|
||||
/* allocate and register the ports */
|
||||
c->port = g_malloc(sizeof(jack_port_t *) * c->nchannels);
|
||||
for (int i = 0; i < c->nchannels; ++i) {
|
||||
|
||||
char port_name[16];
|
||||
snprintf(
|
||||
port_name,
|
||||
sizeof(port_name),
|
||||
c->out ? "output %d" : "input %d",
|
||||
i);
|
||||
|
||||
c->port[i] = jack_port_register(
|
||||
c->client,
|
||||
port_name,
|
||||
JACK_DEFAULT_AUDIO_TYPE,
|
||||
c->out ? JackPortIsOutput : JackPortIsInput,
|
||||
0);
|
||||
}
|
||||
|
||||
/* activate the session */
|
||||
jack_activate(c->client);
|
||||
c->buffersize = jack_get_buffer_size(c->client);
|
||||
|
||||
/*
|
||||
* ensure the buffersize is no smaller then 512 samples, some (all?) qemu
|
||||
* virtual devices do not work correctly otherwise
|
||||
*/
|
||||
if (c->buffersize < 512) {
|
||||
c->buffersize = 512;
|
||||
}
|
||||
|
||||
/* create a 2 period buffer */
|
||||
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2);
|
||||
|
||||
qjack_client_connect_ports(c);
|
||||
c->state = QJACK_STATE_RUNNING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qjack_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
QJackOut *jo = (QJackOut *)hw;
|
||||
Audiodev *dev = (Audiodev *)drv_opaque;
|
||||
|
||||
qjack_client_fini(&jo->c);
|
||||
|
||||
jo->c.out = true;
|
||||
jo->c.enabled = false;
|
||||
jo->c.nchannels = as->nchannels;
|
||||
jo->c.opt = dev->u.jack.out;
|
||||
|
||||
int ret = qjack_client_init(&jo->c);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* report the buffer size to qemu */
|
||||
hw->samples = jo->c.buffersize;
|
||||
|
||||
/* report the audio format we support */
|
||||
struct audsettings os = {
|
||||
.freq = jo->c.freq,
|
||||
.nchannels = jo->c.nchannels,
|
||||
.fmt = AUDIO_FORMAT_F32,
|
||||
.endianness = 0
|
||||
};
|
||||
audio_pcm_init_info(&hw->info, &os);
|
||||
|
||||
dolog("JACK output configured for %dHz (%d samples)\n",
|
||||
jo->c.freq, jo->c.buffersize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qjack_init_in(HWVoiceIn *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
QJackIn *ji = (QJackIn *)hw;
|
||||
Audiodev *dev = (Audiodev *)drv_opaque;
|
||||
|
||||
qjack_client_fini(&ji->c);
|
||||
|
||||
ji->c.out = false;
|
||||
ji->c.enabled = false;
|
||||
ji->c.nchannels = as->nchannels;
|
||||
ji->c.opt = dev->u.jack.in;
|
||||
|
||||
int ret = qjack_client_init(&ji->c);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* report the buffer size to qemu */
|
||||
hw->samples = ji->c.buffersize;
|
||||
|
||||
/* report the audio format we support */
|
||||
struct audsettings is = {
|
||||
.freq = ji->c.freq,
|
||||
.nchannels = ji->c.nchannels,
|
||||
.fmt = AUDIO_FORMAT_F32,
|
||||
.endianness = 0
|
||||
};
|
||||
audio_pcm_init_info(&hw->info, &is);
|
||||
|
||||
dolog("JACK input configured for %dHz (%d samples)\n",
|
||||
ji->c.freq, ji->c.buffersize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qjack_client_fini(QJackClient *c)
|
||||
{
|
||||
switch (c->state) {
|
||||
case QJACK_STATE_RUNNING:
|
||||
jack_deactivate(c->client);
|
||||
/* fallthrough */
|
||||
|
||||
case QJACK_STATE_SHUTDOWN:
|
||||
jack_client_close(c->client);
|
||||
/* fallthrough */
|
||||
|
||||
case QJACK_STATE_DISCONNECTED:
|
||||
break;
|
||||
}
|
||||
|
||||
qjack_buffer_free(&c->fifo);
|
||||
g_free(c->port);
|
||||
|
||||
c->state = QJACK_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
static void qjack_fini_out(HWVoiceOut *hw)
|
||||
{
|
||||
QJackOut *jo = (QJackOut *)hw;
|
||||
qjack_client_fini(&jo->c);
|
||||
}
|
||||
|
||||
static void qjack_fini_in(HWVoiceIn *hw)
|
||||
{
|
||||
QJackIn *ji = (QJackIn *)hw;
|
||||
qjack_client_fini(&ji->c);
|
||||
}
|
||||
|
||||
static void qjack_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
QJackOut *jo = (QJackOut *)hw;
|
||||
jo->c.enabled = enable;
|
||||
}
|
||||
|
||||
static void qjack_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
QJackIn *ji = (QJackIn *)hw;
|
||||
ji->c.enabled = enable;
|
||||
}
|
||||
|
||||
static int qjack_thread_creator(jack_native_thread_t *thread,
|
||||
const pthread_attr_t *attr, void *(*function)(void *), void *arg)
|
||||
{
|
||||
int ret = pthread_create(thread, attr, function, arg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set the name of the thread */
|
||||
pthread_setname_np(*thread, "jack-client");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *qjack_init(Audiodev *dev)
|
||||
{
|
||||
assert(dev->driver == AUDIODEV_DRIVER_JACK);
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void qjack_fini(void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops jack_pcm_ops = {
|
||||
.init_out = qjack_init_out,
|
||||
.fini_out = qjack_fini_out,
|
||||
.write = qjack_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = qjack_enable_out,
|
||||
|
||||
.init_in = qjack_init_in,
|
||||
.fini_in = qjack_fini_in,
|
||||
.read = qjack_read,
|
||||
.enable_in = qjack_enable_in
|
||||
};
|
||||
|
||||
static struct audio_driver jack_driver = {
|
||||
.name = "jack",
|
||||
.descr = "JACK Audio Connection Kit Client",
|
||||
.init = qjack_init,
|
||||
.fini = qjack_fini,
|
||||
.pcm_ops = &jack_pcm_ops,
|
||||
.can_be_default = 1,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof(QJackOut),
|
||||
.voice_size_in = sizeof(QJackIn)
|
||||
};
|
||||
|
||||
static void qjack_error(const char *msg)
|
||||
{
|
||||
dolog("E: %s\n", msg);
|
||||
}
|
||||
|
||||
static void qjack_info(const char *msg)
|
||||
{
|
||||
dolog("I: %s\n", msg);
|
||||
}
|
||||
|
||||
static void register_audio_jack(void)
|
||||
{
|
||||
audio_driver_register(&jack_driver);
|
||||
jack_set_thread_creator(qjack_thread_creator);
|
||||
jack_set_error_function(qjack_error);
|
||||
jack_set_info_function(qjack_info);
|
||||
}
|
||||
type_init(register_audio_jack);
|
||||
+2
-73
@@ -267,82 +267,11 @@ f_sample *mixeng_clip[2][2][2][3] = {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
#define CONV_NATURAL_FLOAT(x) (x)
|
||||
#define CLIP_NATURAL_FLOAT(x) (x)
|
||||
#else
|
||||
/* macros to map [-1.f, 1.f] <-> [INT32_MIN, INT32_MAX + 1] */
|
||||
static const float float_scale = (int64_t)INT32_MAX + 1;
|
||||
#define CONV_NATURAL_FLOAT(x) ((x) * float_scale)
|
||||
|
||||
#ifdef RECIPROCAL
|
||||
static const float float_scale_reciprocal = 1.f / ((int64_t)INT32_MAX + 1);
|
||||
#define CLIP_NATURAL_FLOAT(x) ((x) * float_scale_reciprocal)
|
||||
#else
|
||||
#define CLIP_NATURAL_FLOAT(x) ((x) / float_scale)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void conv_natural_float_to_mono(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
float *in = (float *)src;
|
||||
|
||||
while (samples--) {
|
||||
dst->r = dst->l = CONV_NATURAL_FLOAT(*in++);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
float *in = (float *)src;
|
||||
|
||||
while (samples--) {
|
||||
dst->l = CONV_NATURAL_FLOAT(*in++);
|
||||
dst->r = CONV_NATURAL_FLOAT(*in++);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
t_sample *mixeng_conv_float[2] = {
|
||||
conv_natural_float_to_mono,
|
||||
conv_natural_float_to_stereo,
|
||||
};
|
||||
|
||||
static void clip_natural_float_from_mono(void *dst, const struct st_sample *src,
|
||||
int samples)
|
||||
{
|
||||
float *out = (float *)dst;
|
||||
|
||||
while (samples--) {
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->l + src->r);
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
static void clip_natural_float_from_stereo(
|
||||
void *dst, const struct st_sample *src, int samples)
|
||||
{
|
||||
float *out = (float *)dst;
|
||||
|
||||
while (samples--) {
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->l);
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->r);
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
f_sample *mixeng_clip_float[2] = {
|
||||
clip_natural_float_from_mono,
|
||||
clip_natural_float_from_stereo,
|
||||
};
|
||||
|
||||
void audio_sample_to_uint64(const void *samples, int pos,
|
||||
void audio_sample_to_uint64(void *samples, int pos,
|
||||
uint64_t *left, uint64_t *right)
|
||||
{
|
||||
const struct st_sample *sample = samples;
|
||||
struct st_sample *sample = samples;
|
||||
sample += pos;
|
||||
#ifdef FLOAT_MIXENG
|
||||
error_report(
|
||||
|
||||
+4
-10
@@ -33,24 +33,18 @@ struct st_sample { mixeng_real l; mixeng_real r; };
|
||||
struct mixeng_volume { int mute; int64_t r; int64_t l; };
|
||||
struct st_sample { int64_t l; int64_t r; };
|
||||
#endif
|
||||
typedef struct st_sample st_sample;
|
||||
|
||||
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
|
||||
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
|
||||
|
||||
/* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */
|
||||
extern t_sample *mixeng_conv[2][2][2][3];
|
||||
extern f_sample *mixeng_clip[2][2][2][3];
|
||||
|
||||
/* indices: [stereo] */
|
||||
extern t_sample *mixeng_conv_float[2];
|
||||
extern f_sample *mixeng_clip_float[2];
|
||||
|
||||
void *st_rate_start (int inrate, int outrate);
|
||||
void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
size_t *isamp, size_t *osamp);
|
||||
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
size_t *isamp, size_t *osamp);
|
||||
void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
|
||||
int *isamp, int *osamp);
|
||||
void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
|
||||
int *isamp, int *osamp);
|
||||
void st_rate_stop (void *opaque);
|
||||
void mixeng_clear (struct st_sample *buf, int len);
|
||||
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);
|
||||
|
||||
+12
-10
@@ -41,31 +41,32 @@ static inline mixeng_real glue (conv_, ET) (IN_T v)
|
||||
|
||||
#ifdef RECIPROCAL
|
||||
#ifdef SIGNED
|
||||
return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN));
|
||||
return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN));
|
||||
#else
|
||||
return (nv - HALF) * (2.f / (mixeng_real)IN_MAX);
|
||||
return (nv - HALF) * (1.f / (mixeng_real) IN_MAX);
|
||||
#endif
|
||||
#else /* !RECIPROCAL */
|
||||
#ifdef SIGNED
|
||||
return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f);
|
||||
return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN);
|
||||
#else
|
||||
return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f);
|
||||
return (nv - HALF) / (mixeng_real) IN_MAX;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline IN_T glue (clip_, ET) (mixeng_real v)
|
||||
{
|
||||
if (v >= 1.f) {
|
||||
if (v >= 0.5) {
|
||||
return IN_MAX;
|
||||
} else if (v < -1.f) {
|
||||
}
|
||||
else if (v < -0.5) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f)));
|
||||
return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN)));
|
||||
#else
|
||||
return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF));
|
||||
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -83,9 +84,10 @@ static inline int64_t glue (conv_, ET) (IN_T v)
|
||||
|
||||
static inline IN_T glue (clip_, ET) (int64_t v)
|
||||
{
|
||||
if (v >= 0x7fffffffLL) {
|
||||
if (v >= 0x7f000000) {
|
||||
return IN_MAX;
|
||||
} else if (v < -2147483648LL) {
|
||||
}
|
||||
else if (v < -2147483648LL) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
|
||||
+62
-28
@@ -33,27 +33,43 @@
|
||||
|
||||
typedef struct NoVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
RateCtl rate;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceOut;
|
||||
|
||||
typedef struct NoVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
RateCtl rate;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceIn;
|
||||
|
||||
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
static int no_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
return audio_rate_get_bytes(&hw->info, &no->rate, len);
|
||||
int decr, samples;
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ticks = now - no->old_ticks;
|
||||
bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
bytes = audio_MIN(bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
|
||||
no->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
hw->rpos = (hw->rpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int no_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write(sw, buf, len);
|
||||
}
|
||||
|
||||
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
audio_rate_start(&no->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,22 +78,17 @@ static void no_fini_out (HWVoiceOut *hw)
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static void no_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
|
||||
if (enable) {
|
||||
audio_rate_start(&no->rate);
|
||||
}
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
audio_rate_start(&no->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -86,22 +97,44 @@ static void no_fini_in (HWVoiceIn *hw)
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
|
||||
static int no_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int samples = 0;
|
||||
|
||||
audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame);
|
||||
return bytes;
|
||||
if (dead) {
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes =
|
||||
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
|
||||
no->old_ticks = now;
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
samples = audio_MIN (samples, dead);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
static void no_enable_in(HWVoiceIn *hw, bool enable)
|
||||
static int no_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
/* use custom code here instead of audio_pcm_sw_read() to avoid
|
||||
* useless resampling/mixing */
|
||||
int samples = size >> sw->info.shift;
|
||||
int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
int to_clear = audio_MIN (samples, total);
|
||||
sw->total_hw_samples_acquired += total;
|
||||
audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
|
||||
return to_clear << sw->info.shift;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
audio_rate_start(&no->rate);
|
||||
}
|
||||
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *no_audio_init(Audiodev *dev)
|
||||
@@ -117,14 +150,15 @@ static void no_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops no_pcm_ops = {
|
||||
.init_out = no_init_out,
|
||||
.fini_out = no_fini_out,
|
||||
.run_out = no_run_out,
|
||||
.write = no_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = no_enable_out,
|
||||
.ctl_out = no_ctl_out,
|
||||
|
||||
.init_in = no_init_in,
|
||||
.fini_in = no_fini_in,
|
||||
.run_in = no_run_in,
|
||||
.read = no_read,
|
||||
.enable_in = no_enable_in
|
||||
.ctl_in = no_ctl_in
|
||||
};
|
||||
|
||||
static struct audio_driver no_audio_driver = {
|
||||
|
||||
+262
-162
@@ -40,15 +40,19 @@
|
||||
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int wpos;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int pending;
|
||||
Audiodev *dev;
|
||||
} OSSVoiceOut;
|
||||
|
||||
typedef struct OSSVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
@@ -106,28 +110,33 @@ static void oss_anal_close (int *fdp)
|
||||
|
||||
static void oss_helper_poll_out (void *opaque)
|
||||
{
|
||||
AudioState *s = opaque;
|
||||
audio_run(s, "oss_poll_out");
|
||||
(void) opaque;
|
||||
audio_run ("oss_poll_out");
|
||||
}
|
||||
|
||||
static void oss_helper_poll_in (void *opaque)
|
||||
{
|
||||
AudioState *s = opaque;
|
||||
audio_run(s, "oss_poll_in");
|
||||
(void) opaque;
|
||||
audio_run ("oss_poll_in");
|
||||
}
|
||||
|
||||
static void oss_poll_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
|
||||
qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
|
||||
}
|
||||
|
||||
static void oss_poll_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
|
||||
qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
|
||||
}
|
||||
|
||||
static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
||||
@@ -367,96 +376,97 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static size_t oss_get_available_bytes(OSSVoiceOut *oss)
|
||||
static void oss_write_pending (OSSVoiceOut *oss)
|
||||
{
|
||||
int err;
|
||||
struct count_info cntinfo;
|
||||
assert(oss->mmapped);
|
||||
|
||||
err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
|
||||
}
|
||||
|
||||
static void oss_run_buffer_out(HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
audio_generic_run_buffer_out(hw);
|
||||
}
|
||||
}
|
||||
|
||||
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
if (oss->mmapped) {
|
||||
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
|
||||
return hw->buf_emul + hw->pos_emul;
|
||||
} else {
|
||||
return audio_generic_get_buffer_out(hw, size);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
if (oss->mmapped) {
|
||||
assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
|
||||
|
||||
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
|
||||
return size;
|
||||
} else {
|
||||
return audio_generic_put_buffer_out(hw, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
size_t pos;
|
||||
HWVoiceOut *hw = &oss->hw;
|
||||
|
||||
if (oss->mmapped) {
|
||||
size_t total_len;
|
||||
len = MIN(len, oss_get_available_bytes(oss));
|
||||
|
||||
total_len = len;
|
||||
while (len) {
|
||||
size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
|
||||
memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
|
||||
|
||||
hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul;
|
||||
buf += to_copy;
|
||||
len -= to_copy;
|
||||
}
|
||||
return total_len;
|
||||
return;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (len) {
|
||||
while (oss->pending) {
|
||||
int samples_written;
|
||||
ssize_t bytes_written;
|
||||
void *pcm = advance(buf, pos);
|
||||
int samples_till_end = hw->samples - oss->wpos;
|
||||
int samples_to_write = audio_MIN (oss->pending, samples_till_end);
|
||||
int bytes_to_write = samples_to_write << hw->info.shift;
|
||||
void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
|
||||
|
||||
bytes_written = write(oss->fd, pcm, len);
|
||||
bytes_written = write (oss->fd, pcm, bytes_to_write);
|
||||
if (bytes_written < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
oss_logerr(errno, "failed to write %zu bytes\n",
|
||||
len);
|
||||
oss_logerr (errno, "failed to write %d bytes\n",
|
||||
bytes_to_write);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
pos += bytes_written;
|
||||
if (bytes_written < len) {
|
||||
break;
|
||||
}
|
||||
len -= bytes_written;
|
||||
|
||||
if (bytes_written & hw->info.align) {
|
||||
dolog ("misaligned write asked for %d, but got %zd\n",
|
||||
bytes_to_write, bytes_written);
|
||||
return;
|
||||
}
|
||||
|
||||
samples_written = bytes_written >> hw->info.shift;
|
||||
oss->pending -= samples_written;
|
||||
oss->wpos = (oss->wpos + samples_written) % hw->samples;
|
||||
if (bytes_written - bytes_to_write) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int oss_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
int err, decr;
|
||||
struct audio_buf_info abinfo;
|
||||
struct count_info cntinfo;
|
||||
int bufsize;
|
||||
|
||||
bufsize = hw->samples << hw->info.shift;
|
||||
|
||||
if (oss->mmapped) {
|
||||
int bytes, pos;
|
||||
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pos = hw->rpos << hw->info.shift;
|
||||
bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
|
||||
decr = audio_MIN (bytes >> hw->info.shift, live);
|
||||
}
|
||||
else {
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (abinfo.bytes > bufsize) {
|
||||
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
|
||||
abinfo.bytes = bufsize;
|
||||
}
|
||||
|
||||
if (abinfo.bytes < 0) {
|
||||
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
|
||||
oss->pending += decr;
|
||||
oss_write_pending (oss);
|
||||
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void oss_fini_out (HWVoiceOut *hw)
|
||||
@@ -467,13 +477,18 @@ static void oss_fini_out (HWVoiceOut *hw)
|
||||
ldebug ("oss_fini\n");
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->mmapped && hw->buf_emul) {
|
||||
err = munmap(hw->buf_emul, hw->size_emul);
|
||||
if (err) {
|
||||
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
|
||||
hw->buf_emul, hw->size_emul);
|
||||
if (oss->pcm_buf) {
|
||||
if (oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
hw->buf_emul = NULL;
|
||||
else {
|
||||
g_free (oss->pcm_buf);
|
||||
}
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,29 +530,28 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
|
||||
if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
|
||||
if (obt.nfrags * obt.fragsize & hw->info.align) {
|
||||
dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
|
||||
obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
|
||||
obt.nfrags * obt.fragsize, hw->info.align + 1);
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (oopts->has_try_mmap && oopts->try_mmap) {
|
||||
hw->size_emul = hw->samples * hw->info.bytes_per_frame;
|
||||
hw->buf_emul = mmap(
|
||||
oss->pcm_buf = mmap (
|
||||
NULL,
|
||||
hw->size_emul,
|
||||
hw->samples << hw->info.shift,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
if (hw->buf_emul == MAP_FAILED) {
|
||||
oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
|
||||
hw->size_emul);
|
||||
hw->buf_emul = NULL;
|
||||
} else {
|
||||
if (oss->pcm_buf == MAP_FAILED) {
|
||||
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
|
||||
hw->samples << hw->info.shift);
|
||||
}
|
||||
else {
|
||||
int err;
|
||||
int trig = 0;
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
@@ -557,63 +571,88 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
err = munmap(hw->buf_emul, hw->size_emul);
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
|
||||
hw->buf_emul, hw->size_emul);
|
||||
oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
hw->buf_emul = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
oss->pcm_buf = audio_calloc(__func__,
|
||||
hw->samples,
|
||||
1 << hw->info.shift);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog (
|
||||
"Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int trig;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
if (enable) {
|
||||
hw->poll_mode = opdo->try_poll;
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = opdo->try_poll;
|
||||
|
||||
ldebug("enabling voice\n");
|
||||
if (hw->poll_mode) {
|
||||
oss_poll_out(hw);
|
||||
}
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode) {
|
||||
oss_poll_out (hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return;
|
||||
}
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr(errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
|
||||
return;
|
||||
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (hw->poll_mode) {
|
||||
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
|
||||
hw->poll_mode = 0;
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ldebug ("disabling voice\n");
|
||||
trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
@@ -651,12 +690,19 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
|
||||
if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
|
||||
if (obt.nfrags * obt.fragsize & hw->info.align) {
|
||||
dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
|
||||
obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
|
||||
obt.nfrags * obt.fragsize, hw->info.align + 1);
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->dev = dev;
|
||||
@@ -668,56 +714,111 @@ static void oss_fini_in (HWVoiceIn *hw)
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
g_free(oss->pcm_buf);
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
static int oss_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
size_t pos = 0;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
size_t read_samples = 0;
|
||||
struct {
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ .add = hw->wpos, .len = 0 },
|
||||
{ .add = 0, .len = 0 }
|
||||
};
|
||||
|
||||
while (len) {
|
||||
ssize_t nread;
|
||||
|
||||
void *dst = advance(buf, pos);
|
||||
nread = read(oss->fd, dst, len);
|
||||
|
||||
if (nread == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
|
||||
len, dst);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pos += nread;
|
||||
len -= nread;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pos;
|
||||
if (hw->wpos + dead > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
|
||||
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
|
||||
}
|
||||
else {
|
||||
bufs[0].len = dead << hwshift;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ssize_t nread;
|
||||
|
||||
if (bufs[i].len) {
|
||||
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
|
||||
nread = read (oss->fd, p, bufs[i].len);
|
||||
|
||||
if (nread > 0) {
|
||||
if (nread & hw->info.align) {
|
||||
dolog ("warning: Misaligned read %zd (requested %d), "
|
||||
"alignment %d\n", nread, bufs[i].add << hwshift,
|
||||
hw->info.align + 1);
|
||||
}
|
||||
read_samples += nread >> hwshift;
|
||||
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
|
||||
}
|
||||
|
||||
if (bufs[i].len - nread) {
|
||||
if (nread == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
oss_logerr (
|
||||
errno,
|
||||
"Failed to read %d bytes of audio (to %p)\n",
|
||||
bufs[i].len, p
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
}
|
||||
|
||||
static void oss_enable_in(HWVoiceIn *hw, bool enable)
|
||||
static int oss_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
if (enable) {
|
||||
hw->poll_mode = opdo->try_poll;
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = opdo->try_poll;
|
||||
|
||||
if (hw->poll_mode) {
|
||||
oss_poll_in(hw);
|
||||
if (poll_mode) {
|
||||
oss_poll_in (hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (hw->poll_mode) {
|
||||
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
|
||||
hw->poll_mode = 0;
|
||||
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
|
||||
@@ -753,16 +854,15 @@ static void oss_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops oss_pcm_ops = {
|
||||
.init_out = oss_init_out,
|
||||
.fini_out = oss_fini_out,
|
||||
.run_out = oss_run_out,
|
||||
.write = oss_write,
|
||||
.run_buffer_out = oss_run_buffer_out,
|
||||
.get_buffer_out = oss_get_buffer_out,
|
||||
.put_buffer_out = oss_put_buffer_out,
|
||||
.enable_out = oss_enable_out,
|
||||
.ctl_out = oss_ctl_out,
|
||||
|
||||
.init_in = oss_init_in,
|
||||
.fini_in = oss_fini_in,
|
||||
.run_in = oss_run_in,
|
||||
.read = oss_read,
|
||||
.enable_in = oss_enable_in
|
||||
.ctl_in = oss_ctl_in
|
||||
};
|
||||
|
||||
static struct audio_driver oss_audio_driver = {
|
||||
|
||||
+527
-443
File diff suppressed because it is too large.
Load diff
@@ -28,7 +28,7 @@
|
||||
* Return number of samples processed.
|
||||
*/
|
||||
void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
|
||||
size_t *isamp, size_t *osamp)
|
||||
int *isamp, int *osamp)
|
||||
{
|
||||
struct rate *rate = opaque;
|
||||
struct st_sample *istart, *iend;
|
||||
|
||||
+65
-76
@@ -41,6 +41,8 @@
|
||||
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int live;
|
||||
int decr;
|
||||
} SDLVoiceOut;
|
||||
|
||||
static struct SDLAudioState {
|
||||
@@ -77,14 +79,6 @@ static int aud_to_sdlfmt (AudioFormat fmt)
|
||||
case AUDIO_FORMAT_U16:
|
||||
return AUDIO_U16LSB;
|
||||
|
||||
case AUDIO_FORMAT_S32:
|
||||
return AUDIO_S32LSB;
|
||||
|
||||
/* no unsigned 32-bit support in SDL */
|
||||
|
||||
case AUDIO_FORMAT_F32:
|
||||
return AUDIO_F32LSB;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
@@ -127,26 +121,6 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
break;
|
||||
|
||||
case AUDIO_S32LSB:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S32;
|
||||
break;
|
||||
|
||||
case AUDIO_S32MSB:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_S32;
|
||||
break;
|
||||
|
||||
case AUDIO_F32LSB:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
case AUDIO_F32MSB:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
|
||||
return -1;
|
||||
@@ -210,59 +184,67 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
SDLVoiceOut *sdl = opaque;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
int samples = len >> hw->info.shift;
|
||||
int to_mix, decr;
|
||||
|
||||
if (s->exit) {
|
||||
if (s->exit || !sdl->live) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */
|
||||
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
|
||||
|
||||
while (hw->pending_emul && len) {
|
||||
size_t write_len;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
to_mix = audio_MIN(samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
|
||||
write_len = MIN(MIN(hw->pending_emul, len),
|
||||
hw->size_emul - start);
|
||||
|
||||
memcpy(buf, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= write_len;
|
||||
len -= write_len;
|
||||
buf += write_len;
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip(buf, src, chunk);
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
/* clear remaining buffer that we couldn't fill with data */
|
||||
if (len) {
|
||||
memset(buf, 0, len);
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
|
||||
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
|
||||
if (samples) {
|
||||
memset(buf, 0, samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
|
||||
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \
|
||||
static ret_type glue(sdl_, name)args_decl \
|
||||
{ \
|
||||
ret_type ret; \
|
||||
\
|
||||
SDL_LockAudio(); \
|
||||
\
|
||||
ret = glue(audio_generic_, name)args; \
|
||||
\
|
||||
SDL_UnlockAudio(); \
|
||||
return ret; \
|
||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int sdl_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
int decr;
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
|
||||
SDL_LockAudio();
|
||||
|
||||
if (sdl->decr > live) {
|
||||
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
||||
sdl->decr,
|
||||
live,
|
||||
sdl->live);
|
||||
}
|
||||
|
||||
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size), *size = 0, sdl_unlock)
|
||||
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
|
||||
/*nothing*/, sdl_unlock_and_post)
|
||||
SDL_WRAPPER_FUNC(write, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
|
||||
/*nothing*/, sdl_unlock_and_post)
|
||||
decr = audio_MIN (sdl->decr, live);
|
||||
sdl->decr -= decr;
|
||||
|
||||
#undef SDL_WRAPPER_FUNC
|
||||
sdl->live = live;
|
||||
|
||||
SDL_UnlockAudio();
|
||||
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void sdl_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
@@ -313,9 +295,20 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdl_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
SDL_PauseAudio(!enable);
|
||||
(void) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
SDL_PauseAudio (0);
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
SDL_PauseAudio (1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *sdl_audio_init(Audiodev *dev)
|
||||
@@ -348,13 +341,9 @@ static void sdl_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
.init_out = sdl_init_out,
|
||||
.fini_out = sdl_fini_out,
|
||||
/* wrapper for audio_generic_write */
|
||||
.write = sdl_write,
|
||||
/* wrapper for audio_generic_get_buffer_out */
|
||||
.get_buffer_out = sdl_get_buffer_out,
|
||||
/* wrapper for audio_generic_put_buffer_out */
|
||||
.put_buffer_out = sdl_put_buffer_out,
|
||||
.enable_out = sdl_enable_out,
|
||||
.run_out = sdl_run_out,
|
||||
.write = sdl_write_out,
|
||||
.ctl_out = sdl_ctl_out,
|
||||
};
|
||||
|
||||
static struct audio_driver sdl_audio_driver = {
|
||||
|
||||
+180
-87
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -40,21 +41,27 @@
|
||||
#define LINE_IN_SAMPLES (256 * 4)
|
||||
#endif
|
||||
|
||||
typedef struct SpiceRateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
} SpiceRateCtl;
|
||||
|
||||
typedef struct SpiceVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
SpicePlaybackInstance sin;
|
||||
RateCtl rate;
|
||||
SpiceRateCtl rate;
|
||||
int active;
|
||||
uint32_t *frame;
|
||||
uint32_t fpos;
|
||||
uint32_t *fpos;
|
||||
uint32_t fsize;
|
||||
} SpiceVoiceOut;
|
||||
|
||||
typedef struct SpiceVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
SpiceRecordInstance sin;
|
||||
RateCtl rate;
|
||||
SpiceRateCtl rate;
|
||||
int active;
|
||||
uint32_t samples[LINE_IN_SAMPLES];
|
||||
} SpiceVoiceIn;
|
||||
|
||||
static const SpicePlaybackInterface playback_sif = {
|
||||
@@ -84,6 +91,32 @@ static void spice_audio_fini (void *opaque)
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void rate_start (SpiceRateCtl *rate)
|
||||
{
|
||||
memset (rate, 0, sizeof (*rate));
|
||||
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
|
||||
{
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
int64_t samples;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ticks = now - rate->start_ticks;
|
||||
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
samples = (bytes - rate->bytes_sent) >> info->shift;
|
||||
if (samples < 0 || samples > 65536) {
|
||||
error_report("Resetting rate control (%" PRId64 " samples)", samples);
|
||||
rate_start(rate);
|
||||
samples = 0;
|
||||
}
|
||||
rate->bytes_sent += samples << info->shift;
|
||||
return samples;
|
||||
}
|
||||
|
||||
/* playback */
|
||||
|
||||
static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||
@@ -120,78 +153,98 @@ static void line_out_fini (HWVoiceOut *hw)
|
||||
spice_server_remove_interface (&out->sin.base);
|
||||
}
|
||||
|
||||
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
|
||||
static int line_out_run (HWVoiceOut *hw, int live)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
int rpos, decr;
|
||||
int samples;
|
||||
|
||||
if (!out->frame) {
|
||||
spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
|
||||
out->fpos = 0;
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (out->frame) {
|
||||
*size = audio_rate_get_bytes(
|
||||
&hw->info, &out->rate,
|
||||
(out->fsize - out->fpos) * hw->info.bytes_per_frame);
|
||||
} else {
|
||||
audio_rate_start(&out->rate);
|
||||
decr = rate_get_samples (&hw->info, &out->rate);
|
||||
decr = audio_MIN (live, decr);
|
||||
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int len = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
if (!out->frame) {
|
||||
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
|
||||
out->fpos = out->frame;
|
||||
}
|
||||
if (out->frame) {
|
||||
len = audio_MIN (len, out->fsize);
|
||||
hw->clip (out->fpos, hw->mix_buf + rpos, len);
|
||||
out->fsize -= len;
|
||||
out->fpos += len;
|
||||
if (out->fsize == 0) {
|
||||
spice_server_playback_put_samples (&out->sin, out->frame);
|
||||
out->frame = out->fpos = NULL;
|
||||
}
|
||||
}
|
||||
rpos = (rpos + len) % hw->samples;
|
||||
samples -= len;
|
||||
}
|
||||
return out->frame + out->fpos;
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
|
||||
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
|
||||
|
||||
assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
|
||||
out->fpos += size >> 2;
|
||||
|
||||
if (out->fpos == out->fsize) { /* buffer full */
|
||||
spice_server_playback_put_samples(&out->sin, out->frame);
|
||||
out->frame = NULL;
|
||||
}
|
||||
|
||||
return size;
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static void line_out_enable(HWVoiceOut *hw, bool enable)
|
||||
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
|
||||
if (enable) {
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (out->active) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
out->active = 1;
|
||||
audio_rate_start(&out->rate);
|
||||
rate_start (&out->rate);
|
||||
spice_server_playback_start (&out->sin);
|
||||
} else {
|
||||
break;
|
||||
case VOICE_DISABLE:
|
||||
if (!out->active) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
out->active = 0;
|
||||
if (out->frame) {
|
||||
memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2);
|
||||
memset (out->fpos, 0, out->fsize << 2);
|
||||
spice_server_playback_put_samples (&out->sin, out->frame);
|
||||
out->frame = NULL;
|
||||
out->frame = out->fpos = NULL;
|
||||
}
|
||||
spice_server_playback_stop (&out->sin);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
static void line_out_volume(HWVoiceOut *hw, Volume *vol)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
|
||||
uint16_t svol[2];
|
||||
SWVoiceOut *sw;
|
||||
va_list ap;
|
||||
uint16_t vol[2];
|
||||
|
||||
assert(vol->channels == 2);
|
||||
svol[0] = vol->vol[0] * 257;
|
||||
svol[1] = vol->vol[1] * 257;
|
||||
spice_server_playback_set_volume(&out->sin, 2, svol);
|
||||
spice_server_playback_set_mute(&out->sin, vol->mute);
|
||||
}
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceOut *);
|
||||
va_end (ap);
|
||||
|
||||
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
|
||||
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
|
||||
spice_server_playback_set_volume (&out->sin, 2, vol);
|
||||
spice_server_playback_set_mute (&out->sin, sw->vol.mute);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* record */
|
||||
|
||||
@@ -228,74 +281,111 @@ static void line_in_fini (HWVoiceIn *hw)
|
||||
spice_server_remove_interface (&in->sin.base);
|
||||
}
|
||||
|
||||
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
static int line_in_run (HWVoiceIn *hw)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
|
||||
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
|
||||
int num_samples;
|
||||
int ready;
|
||||
int len[2];
|
||||
uint64_t delta_samp;
|
||||
const uint32_t *samples;
|
||||
|
||||
/* XXX: do we need this? */
|
||||
if (ready == 0) {
|
||||
memset(buf, 0, to_read << 2);
|
||||
ready = to_read;
|
||||
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ready << 2;
|
||||
delta_samp = rate_get_samples (&hw->info, &in->rate);
|
||||
num_samples = audio_MIN (num_samples, delta_samp);
|
||||
|
||||
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
|
||||
samples = in->samples;
|
||||
if (ready == 0) {
|
||||
static const uint32_t silence[LINE_IN_SAMPLES];
|
||||
samples = silence;
|
||||
ready = LINE_IN_SAMPLES;
|
||||
}
|
||||
|
||||
num_samples = audio_MIN (ready, num_samples);
|
||||
|
||||
if (hw->wpos + num_samples > hw->samples) {
|
||||
len[0] = hw->samples - hw->wpos;
|
||||
len[1] = num_samples - len[0];
|
||||
} else {
|
||||
len[0] = num_samples;
|
||||
len[1] = 0;
|
||||
}
|
||||
|
||||
hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
|
||||
|
||||
if (len[1]) {
|
||||
hw->conv (hw->conv_buf, samples + len[0], len[1]);
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + num_samples) % hw->samples;
|
||||
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
static void line_in_enable(HWVoiceIn *hw, bool enable)
|
||||
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
|
||||
if (enable) {
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (in->active) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
in->active = 1;
|
||||
audio_rate_start(&in->rate);
|
||||
rate_start (&in->rate);
|
||||
spice_server_record_start (&in->sin);
|
||||
} else {
|
||||
break;
|
||||
case VOICE_DISABLE:
|
||||
if (!in->active) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
in->active = 0;
|
||||
spice_server_record_stop (&in->sin);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
|
||||
static void line_in_volume(HWVoiceIn *hw, Volume *vol)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
|
||||
uint16_t svol[2];
|
||||
SWVoiceIn *sw;
|
||||
va_list ap;
|
||||
uint16_t vol[2];
|
||||
|
||||
assert(vol->channels == 2);
|
||||
svol[0] = vol->vol[0] * 257;
|
||||
svol[1] = vol->vol[1] * 257;
|
||||
spice_server_record_set_volume(&in->sin, 2, svol);
|
||||
spice_server_record_set_mute(&in->sin, vol->mute);
|
||||
}
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceIn *);
|
||||
va_end (ap);
|
||||
|
||||
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
|
||||
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
|
||||
spice_server_record_set_volume (&in->sin, 2, vol);
|
||||
spice_server_record_set_mute (&in->sin, sw->vol.mute);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops audio_callbacks = {
|
||||
.init_out = line_out_init,
|
||||
.fini_out = line_out_fini,
|
||||
.write = audio_generic_write,
|
||||
.get_buffer_out = line_out_get_buffer,
|
||||
.put_buffer_out = line_out_put_buffer,
|
||||
.enable_out = line_out_enable,
|
||||
#if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \
|
||||
(SPICE_INTERFACE_PLAYBACK_MINOR >= 2)
|
||||
.volume_out = line_out_volume,
|
||||
#endif
|
||||
.run_out = line_out_run,
|
||||
.write = line_out_write,
|
||||
.ctl_out = line_out_ctl,
|
||||
|
||||
.init_in = line_in_init,
|
||||
.fini_in = line_in_fini,
|
||||
.run_in = line_in_run,
|
||||
.read = line_in_read,
|
||||
.enable_in = line_in_enable,
|
||||
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
|
||||
.volume_in = line_in_volume,
|
||||
#endif
|
||||
.ctl_in = line_in_ctl,
|
||||
};
|
||||
|
||||
static struct audio_driver spice_audio_driver = {
|
||||
@@ -308,6 +398,9 @@ static struct audio_driver spice_audio_driver = {
|
||||
.max_voices_in = 1,
|
||||
.voice_size_out = sizeof (SpiceVoiceOut),
|
||||
.voice_size_in = sizeof (SpiceVoiceIn),
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
.ctl_caps = VOICE_VOLUME_CAP
|
||||
#endif
|
||||
};
|
||||
|
||||
void qemu_spice_audio_init (void)
|
||||
|
||||
+63
-20
@@ -35,23 +35,58 @@
|
||||
typedef struct WAVVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
FILE *f;
|
||||
RateCtl rate;
|
||||
int64_t old_ticks;
|
||||
void *pcm_buf;
|
||||
int total_samples;
|
||||
} WAVVoiceOut;
|
||||
|
||||
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
|
||||
static int wav_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
|
||||
assert(bytes % hw->info.bytes_per_frame == 0);
|
||||
int rpos, decr, samples;
|
||||
uint8_t *dst;
|
||||
struct st_sample *src;
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t ticks = now - wav->old_ticks;
|
||||
int64_t bytes =
|
||||
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
|
||||
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
|
||||
dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
|
||||
bytes, strerror(errno));
|
||||
if (bytes > INT_MAX) {
|
||||
samples = INT_MAX >> hw->info.shift;
|
||||
}
|
||||
else {
|
||||
samples = bytes >> hw->info.shift;
|
||||
}
|
||||
|
||||
wav->total_samples += bytes / hw->info.bytes_per_frame;
|
||||
return bytes;
|
||||
wav->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
|
||||
dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
|
||||
convert_samples << hw->info.shift, strerror (errno));
|
||||
}
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
wav->total_samples += convert_samples;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
/* VICE code: Store number as little endian. */
|
||||
@@ -107,6 +142,13 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
audio_pcm_init_info (&hw->info, &wav_as);
|
||||
|
||||
hw->samples = 1024;
|
||||
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
if (!wav->pcm_buf) {
|
||||
dolog ("Could not allocate buffer (%d bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
return -1;
|
||||
}
|
||||
|
||||
le_store (hdr + 22, hw->info.nchannels, 2);
|
||||
le_store (hdr + 24, hw->info.freq, 4);
|
||||
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||
@@ -116,6 +158,8 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
if (!wav->f) {
|
||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
wav_path, strerror(errno));
|
||||
g_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -124,8 +168,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio_rate_start(&wav->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -134,7 +176,7 @@ static void wav_fini_out (HWVoiceOut *hw)
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
uint8_t rlen[4];
|
||||
uint8_t dlen[4];
|
||||
uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame;
|
||||
uint32_t datalen = wav->total_samples << hw->info.shift;
|
||||
uint32_t rifflen = datalen + 36;
|
||||
|
||||
if (!wav->f) {
|
||||
@@ -171,15 +213,16 @@ static void wav_fini_out (HWVoiceOut *hw)
|
||||
wav->f, strerror (errno));
|
||||
}
|
||||
wav->f = NULL;
|
||||
|
||||
g_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static void wav_enable_out(HWVoiceOut *hw, bool enable)
|
||||
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
|
||||
if (enable) {
|
||||
audio_rate_start(&wav->rate);
|
||||
}
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *wav_audio_init(Audiodev *dev)
|
||||
@@ -196,9 +239,9 @@ static void wav_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops wav_pcm_ops = {
|
||||
.init_out = wav_init_out,
|
||||
.fini_out = wav_fini_out,
|
||||
.run_out = wav_run_out,
|
||||
.write = wav_write_out,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = wav_enable_out,
|
||||
.ctl_out = wav_ctl_out,
|
||||
};
|
||||
|
||||
static struct audio_driver wav_audio_driver = {
|
||||
|
||||
+5
-4
@@ -1,4 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -71,7 +72,7 @@ static void wav_destroy (void *opaque)
|
||||
g_free (wav->path);
|
||||
}
|
||||
|
||||
static void wav_capture(void *opaque, const void *buf, int size)
|
||||
static void wav_capture (void *opaque, void *buf, int size)
|
||||
{
|
||||
WAVState *wav = opaque;
|
||||
|
||||
@@ -104,8 +105,8 @@ static struct capture_ops wav_capture_ops = {
|
||||
.info = wav_capture_info
|
||||
};
|
||||
|
||||
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
|
||||
int freq, int bits, int nchannels)
|
||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
int bits, int nchannels)
|
||||
{
|
||||
WAVState *wav;
|
||||
uint8_t hdr[] = {
|
||||
@@ -170,7 +171,7 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
cap = AUD_add_capture(state, &as, &ops, wav);
|
||||
cap = AUD_add_capture (&as, &ops, wav);
|
||||
if (!cap) {
|
||||
error_report("Failed to add audio capture");
|
||||
goto error_free;
|
||||
|
||||
+3
-2
@@ -124,12 +124,13 @@ qauthz_list_class_init(ObjectClass *oc, void *data)
|
||||
"QAuthZListPolicy",
|
||||
&QAuthZListPolicy_lookup,
|
||||
qauthz_list_prop_get_policy,
|
||||
qauthz_list_prop_set_policy);
|
||||
qauthz_list_prop_set_policy,
|
||||
NULL);
|
||||
|
||||
object_class_property_add(oc, "rules", "QAuthZListRule",
|
||||
qauthz_list_prop_get_rules,
|
||||
qauthz_list_prop_set_rules,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
|
||||
authz->is_allowed = qauthz_list_is_allowed;
|
||||
}
|
||||
|
||||
+5
-3
@@ -221,10 +221,12 @@ qauthz_list_file_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
object_class_property_add_str(oc, "filename",
|
||||
qauthz_list_file_prop_get_filename,
|
||||
qauthz_list_file_prop_set_filename);
|
||||
qauthz_list_file_prop_set_filename,
|
||||
NULL);
|
||||
object_class_property_add_bool(oc, "refresh",
|
||||
qauthz_list_file_prop_get_refresh,
|
||||
qauthz_list_file_prop_set_refresh);
|
||||
qauthz_list_file_prop_set_refresh,
|
||||
NULL);
|
||||
|
||||
authz->is_allowed = qauthz_list_file_is_allowed;
|
||||
}
|
||||
@@ -237,7 +239,7 @@ qauthz_list_file_init(Object *obj)
|
||||
|
||||
authz->file_watch = -1;
|
||||
#ifdef CONFIG_INOTIFY1
|
||||
authz->refresh = true;
|
||||
authz->refresh = TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -107,7 +107,8 @@ qauthz_pam_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
object_class_property_add_str(oc, "service",
|
||||
qauthz_pam_prop_get_service,
|
||||
qauthz_pam_prop_set_service);
|
||||
qauthz_pam_prop_set_service,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -74,7 +74,8 @@ qauthz_simple_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
object_class_property_add_str(oc, "identity",
|
||||
qauthz_simple_prop_get_identity,
|
||||
qauthz_simple_prop_set_identity);
|
||||
qauthz_simple_prop_set_identity,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
source tpm/Kconfig
|
||||
@@ -1,7 +1,7 @@
|
||||
common-obj-y += rng.o rng-egd.o rng-builtin.o
|
||||
common-obj-y += rng.o rng-egd.o
|
||||
common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
|
||||
common-obj-$(CONFIG_TPM) += tpm/
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += hostmem.o hostmem-ram.o
|
||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
||||
@@ -17,7 +17,3 @@ endif
|
||||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
|
||||
|
||||
common-obj-$(CONFIG_GIO) += dbus-vmstate.o
|
||||
dbus-vmstate.o-cflags = $(GIO_CFLAGS)
|
||||
dbus-vmstate.o-libs = $(GIO_LIBS)
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/cryptodev.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/error.h"
|
||||
#include "standard-headers/linux/virtio_crypto.h"
|
||||
#include "crypto/cipher.h"
|
||||
@@ -282,7 +283,12 @@ static int cryptodev_builtin_sym_close_session(
|
||||
CryptoDevBackendBuiltin *builtin =
|
||||
CRYPTODEV_BACKEND_BUILTIN(backend);
|
||||
|
||||
assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);
|
||||
if (session_id >= MAX_NUM_SESSIONS ||
|
||||
builtin->sessions[session_id] == NULL) {
|
||||
error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
|
||||
session_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
|
||||
g_free(builtin->sessions[session_id]);
|
||||
@@ -351,7 +357,8 @@ static void cryptodev_builtin_cleanup(
|
||||
|
||||
for (i = 0; i < MAX_NUM_SESSIONS; i++) {
|
||||
if (builtin->sessions[i] != NULL) {
|
||||
cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort);
|
||||
cryptodev_builtin_sym_close_session(
|
||||
backend, i, 0, errp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -152,7 +153,7 @@ cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s,
|
||||
return chr;
|
||||
}
|
||||
|
||||
static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event)
|
||||
static void cryptodev_vhost_user_event(void *opaque, int event)
|
||||
{
|
||||
CryptoDevBackendVhostUser *s = opaque;
|
||||
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
|
||||
@@ -171,11 +172,6 @@ static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event)
|
||||
b->ready = false;
|
||||
cryptodev_vhost_user_stop(queues, s);
|
||||
break;
|
||||
case CHR_EVENT_BREAK:
|
||||
case CHR_EVENT_MUX_IN:
|
||||
case CHR_EVENT_MUX_OUT:
|
||||
/* Ignore */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +205,8 @@ static void cryptodev_vhost_user_init(
|
||||
backend->conf.peers.ccs[i] = cc;
|
||||
|
||||
if (i == 0) {
|
||||
if (!qemu_chr_fe_init(&s->chr, chr, errp)) {
|
||||
if (!qemu_chr_fe_init(&s->chr, chr, &local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -339,7 +336,8 @@ static void cryptodev_vhost_user_instance_int(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "chardev",
|
||||
cryptodev_vhost_user_get_chardev,
|
||||
cryptodev_vhost_user_set_chardev);
|
||||
cryptodev_vhost_user_set_chardev,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void cryptodev_vhost_user_finalize(Object *obj)
|
||||
|
||||
+22
-8
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/cryptodev.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/config-file.h"
|
||||
@@ -154,17 +155,21 @@ cryptodev_backend_set_queues(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj);
|
||||
Error *local_err = NULL;
|
||||
uint32_t value;
|
||||
|
||||
if (!visit_type_uint32(v, name, &value, errp)) {
|
||||
return;
|
||||
visit_type_uint32(v, name, &value, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
if (!value) {
|
||||
error_setg(errp, "Property '%s.%s' doesn't take value '%" PRIu32 "'",
|
||||
object_get_typename(obj), name, value);
|
||||
return;
|
||||
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
|
||||
PRIu32 "'", object_get_typename(obj), name, value);
|
||||
goto out;
|
||||
}
|
||||
backend->conf.peers.queues = value;
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -172,10 +177,19 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc);
|
||||
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (bc->init) {
|
||||
bc->init(backend, errp);
|
||||
bc->init(backend, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used)
|
||||
@@ -209,9 +223,9 @@ static void cryptodev_backend_instance_init(Object *obj)
|
||||
object_property_add(obj, "queues", "uint32",
|
||||
cryptodev_backend_get_queues,
|
||||
cryptodev_backend_set_queues,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
/* Initialize devices' queues property to 1 */
|
||||
object_property_set_int(obj, "queues", 1, NULL);
|
||||
object_property_set_int(obj, 1, "queues", NULL);
|
||||
}
|
||||
|
||||
static void cryptodev_backend_finalize(Object *obj)
|
||||
|
||||
@@ -1,509 +0,0 @@
|
||||
/*
|
||||
* QEMU dbus-vmstate
|
||||
*
|
||||
* Copyright (C) 2019 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/dbus.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
typedef struct DBusVMState DBusVMState;
|
||||
typedef struct DBusVMStateClass DBusVMStateClass;
|
||||
|
||||
#define TYPE_DBUS_VMSTATE "dbus-vmstate"
|
||||
#define DBUS_VMSTATE(obj) \
|
||||
OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE)
|
||||
#define DBUS_VMSTATE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE)
|
||||
#define DBUS_VMSTATE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE)
|
||||
|
||||
struct DBusVMStateClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct DBusVMState {
|
||||
Object parent;
|
||||
|
||||
GDBusConnection *bus;
|
||||
char *dbus_addr;
|
||||
char *id_list;
|
||||
|
||||
uint32_t data_size;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
static const GDBusPropertyInfo vmstate_property_info[] = {
|
||||
{ -1, (char *) "Id", (char *) "s",
|
||||
G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
|
||||
};
|
||||
|
||||
static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
|
||||
&vmstate_property_info[0],
|
||||
NULL
|
||||
};
|
||||
|
||||
static const GDBusInterfaceInfo vmstate1_interface_info = {
|
||||
-1,
|
||||
(char *) "org.qemu.VMState1",
|
||||
(GDBusMethodInfo **) NULL,
|
||||
(GDBusSignalInfo **) NULL,
|
||||
(GDBusPropertyInfo **) &vmstate_property_info_pointers,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
|
||||
|
||||
static GHashTable *
|
||||
get_id_list_set(DBusVMState *self)
|
||||
{
|
||||
g_auto(GStrv) ids = NULL;
|
||||
g_autoptr(GHashTable) set = NULL;
|
||||
int i;
|
||||
|
||||
if (!self->id_list) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ids = g_strsplit(self->id_list, ",", -1);
|
||||
set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||
for (i = 0; ids[i]; i++) {
|
||||
g_hash_table_add(set, ids[i]);
|
||||
ids[i] = NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&set);
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
dbus_get_proxies(DBusVMState *self, GError **err)
|
||||
{
|
||||
g_autoptr(GHashTable) proxies = NULL;
|
||||
g_autoptr(GHashTable) ids = NULL;
|
||||
g_auto(GStrv) names = NULL;
|
||||
Error *error = NULL;
|
||||
size_t i;
|
||||
|
||||
ids = get_id_list_set(self);
|
||||
proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, g_object_unref);
|
||||
|
||||
names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
|
||||
if (!names) {
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
|
||||
error_get_pretty(error));
|
||||
error_free(error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; names[i]; i++) {
|
||||
g_autoptr(GDBusProxy) proxy = NULL;
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autofree char *id = NULL;
|
||||
size_t size;
|
||||
|
||||
proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
|
||||
(GDBusInterfaceInfo *) &vmstate1_interface_info,
|
||||
names[i],
|
||||
"/org/qemu/VMState1",
|
||||
"org.qemu.VMState1",
|
||||
NULL, err);
|
||||
if (!proxy) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = g_dbus_proxy_get_cached_property(proxy, "Id");
|
||||
if (!result) {
|
||||
g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"VMState Id property is missing.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = g_variant_dup_string(result, &size);
|
||||
if (ids && !g_hash_table_remove(ids, id)) {
|
||||
g_clear_pointer(&id, g_free);
|
||||
g_clear_object(&proxy);
|
||||
continue;
|
||||
}
|
||||
if (size == 0 || size >= 256) {
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"VMState Id '%s' is invalid.", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_hash_table_insert(proxies, id, proxy)) {
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Duplicated VMState Id '%s'", id);
|
||||
return NULL;
|
||||
}
|
||||
id = NULL;
|
||||
proxy = NULL;
|
||||
|
||||
g_clear_pointer(&result, g_variant_unref);
|
||||
}
|
||||
|
||||
if (ids) {
|
||||
g_autofree char **left = NULL;
|
||||
|
||||
left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
|
||||
if (*left) {
|
||||
g_autofree char *leftids = g_strjoinv(",", left);
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Required VMState Id are missing: %s", leftids);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return g_steal_pointer(&proxies);
|
||||
}
|
||||
|
||||
static int
|
||||
dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
|
||||
{
|
||||
g_autoptr(GError) err = NULL;
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autoptr(GVariant) value = NULL;
|
||||
|
||||
value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
|
||||
data, size, sizeof(char));
|
||||
result = g_dbus_proxy_call_sync(proxy, "Load",
|
||||
g_variant_new("(@ay)",
|
||||
g_steal_pointer(&value)),
|
||||
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||
-1, NULL, &err);
|
||||
if (!result) {
|
||||
error_report("%s: Failed to Load: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbus_vmstate_post_load(void *opaque, int version_id)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(opaque);
|
||||
g_autoptr(GInputStream) m = NULL;
|
||||
g_autoptr(GDataInputStream) s = NULL;
|
||||
g_autoptr(GError) err = NULL;
|
||||
g_autoptr(GHashTable) proxies = NULL;
|
||||
uint32_t nelem;
|
||||
|
||||
trace_dbus_vmstate_post_load(version_id);
|
||||
|
||||
proxies = dbus_get_proxies(self, &err);
|
||||
if (!proxies) {
|
||||
error_report("%s: Failed to get proxies: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
|
||||
s = g_data_input_stream_new(m);
|
||||
g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
|
||||
|
||||
nelem = g_data_input_stream_read_uint32(s, NULL, &err);
|
||||
if (err) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (nelem > 0) {
|
||||
GDBusProxy *proxy = NULL;
|
||||
uint32_t len;
|
||||
gsize bytes_read, avail;
|
||||
char id[256];
|
||||
|
||||
len = g_data_input_stream_read_uint32(s, NULL, &err);
|
||||
if (err) {
|
||||
goto error;
|
||||
}
|
||||
if (len >= 256) {
|
||||
error_report("%s: Invalid DBus vmstate proxy name %u",
|
||||
__func__, len);
|
||||
return -1;
|
||||
}
|
||||
if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
|
||||
&bytes_read, NULL, &err)) {
|
||||
goto error;
|
||||
}
|
||||
g_return_val_if_fail(bytes_read == len, -1);
|
||||
id[len] = 0;
|
||||
|
||||
trace_dbus_vmstate_loading(id);
|
||||
|
||||
proxy = g_hash_table_lookup(proxies, id);
|
||||
if (!proxy) {
|
||||
error_report("%s: Failed to find proxy Id '%s'", __func__, id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = g_data_input_stream_read_uint32(s, NULL, &err);
|
||||
avail = g_buffered_input_stream_get_available(
|
||||
G_BUFFERED_INPUT_STREAM(s));
|
||||
|
||||
if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
|
||||
error_report("%s: Invalid vmstate size: %u", __func__, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dbus_load_state_proxy(proxy,
|
||||
g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
|
||||
NULL),
|
||||
len) < 0) {
|
||||
error_report("%s: Failed to restore Id '%s'", __func__, id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
nelem -= 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
error_report("%s: Failed to read from stream: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_save_state_proxy(gpointer key,
|
||||
gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDataOutputStream *s = user_data;
|
||||
const char *id = key;
|
||||
GDBusProxy *proxy = value;
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autoptr(GVariant) child = NULL;
|
||||
g_autoptr(GError) err = NULL;
|
||||
const uint8_t *data;
|
||||
gsize size;
|
||||
|
||||
trace_dbus_vmstate_saving(id);
|
||||
|
||||
result = g_dbus_proxy_call_sync(proxy, "Save",
|
||||
NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||
-1, NULL, &err);
|
||||
if (!result) {
|
||||
error_report("%s: Failed to Save: %s", __func__, err->message);
|
||||
return;
|
||||
}
|
||||
|
||||
child = g_variant_get_child_value(result, 0);
|
||||
data = g_variant_get_fixed_array(child, &size, sizeof(char));
|
||||
if (!data) {
|
||||
error_report("%s: Failed to Save: not a byte array", __func__);
|
||||
return;
|
||||
}
|
||||
if (size > DBUS_VMSTATE_SIZE_LIMIT) {
|
||||
error_report("%s: Too large vmstate data to save: %zu",
|
||||
__func__, (size_t)size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
|
||||
!g_data_output_stream_put_string(s, id, NULL, &err) ||
|
||||
!g_data_output_stream_put_uint32(s, size, NULL, &err) ||
|
||||
!g_output_stream_write_all(G_OUTPUT_STREAM(s),
|
||||
data, size, NULL, NULL, &err)) {
|
||||
error_report("%s: Failed to write to stream: %s",
|
||||
__func__, err->message);
|
||||
}
|
||||
}
|
||||
|
||||
static int dbus_vmstate_pre_save(void *opaque)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(opaque);
|
||||
g_autoptr(GOutputStream) m = NULL;
|
||||
g_autoptr(GDataOutputStream) s = NULL;
|
||||
g_autoptr(GHashTable) proxies = NULL;
|
||||
g_autoptr(GError) err = NULL;
|
||||
|
||||
trace_dbus_vmstate_pre_save();
|
||||
|
||||
proxies = dbus_get_proxies(self, &err);
|
||||
if (!proxies) {
|
||||
error_report("%s: Failed to get proxies: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m = g_memory_output_stream_new_resizable();
|
||||
s = g_data_output_stream_new(m);
|
||||
g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
|
||||
|
||||
if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
|
||||
NULL, &err)) {
|
||||
error_report("%s: Failed to write to stream: %s",
|
||||
__func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
|
||||
|
||||
if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
|
||||
> UINT32_MAX) {
|
||||
error_report("%s: DBus vmstate buffer is too large", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
|
||||
error_report("%s: Failed to close stream: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_free(self->data);
|
||||
self->data_size =
|
||||
g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
|
||||
self->data =
|
||||
g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription dbus_vmstate = {
|
||||
.name = TYPE_DBUS_VMSTATE,
|
||||
.version_id = 0,
|
||||
.pre_save = dbus_vmstate_pre_save,
|
||||
.post_load = dbus_vmstate_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(data_size, DBusVMState),
|
||||
VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
dbus_vmstate_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(uc);
|
||||
g_autoptr(GError) err = NULL;
|
||||
|
||||
if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
|
||||
error_setg(errp, "There is already an instance of %s",
|
||||
TYPE_DBUS_VMSTATE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->dbus_addr) {
|
||||
error_setg(errp, QERR_MISSING_PARAMETER, "addr");
|
||||
return;
|
||||
}
|
||||
|
||||
self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
|
||||
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
|
||||
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
|
||||
NULL, NULL, &err);
|
||||
if (err) {
|
||||
error_setg(errp, "failed to connect to DBus: '%s'", err->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
|
||||
&dbus_vmstate, self) < 0) {
|
||||
error_setg(errp, "Failed to register vmstate");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_vmstate_finalize(Object *o)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
|
||||
|
||||
g_clear_object(&self->bus);
|
||||
g_free(self->dbus_addr);
|
||||
g_free(self->id_list);
|
||||
g_free(self->data);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_dbus_addr(Object *o, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
return g_strdup(self->dbus_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
set_dbus_addr(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
g_free(self->dbus_addr);
|
||||
self->dbus_addr = g_strdup(str);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_id_list(Object *o, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
return g_strdup(self->id_list);
|
||||
}
|
||||
|
||||
static void
|
||||
set_id_list(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
g_free(self->id_list);
|
||||
self->id_list = g_strdup(str);
|
||||
}
|
||||
|
||||
static char *
|
||||
dbus_vmstate_get_id(VMStateIf *vmif)
|
||||
{
|
||||
return g_strdup(TYPE_DBUS_VMSTATE);
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_vmstate_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
|
||||
|
||||
ucc->complete = dbus_vmstate_complete;
|
||||
vc->get_id = dbus_vmstate_get_id;
|
||||
|
||||
object_class_property_add_str(oc, "addr",
|
||||
get_dbus_addr, set_dbus_addr);
|
||||
object_class_property_add_str(oc, "id-list",
|
||||
get_id_list, set_id_list);
|
||||
}
|
||||
|
||||
static const TypeInfo dbus_vmstate_info = {
|
||||
.name = TYPE_DBUS_VMSTATE,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(DBusVMState),
|
||||
.instance_finalize = dbus_vmstate_finalize,
|
||||
.class_size = sizeof(DBusVMStateClass),
|
||||
.class_init = dbus_vmstate_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ TYPE_VMSTATE_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
register_types(void)
|
||||
{
|
||||
type_register_static(&dbus_vmstate_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
+53
-10
@@ -18,6 +18,13 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
/* hostmem-file.c */
|
||||
/**
|
||||
* @TYPE_MEMORY_BACKEND_FILE:
|
||||
* name of backend that uses mmap on a file descriptor
|
||||
*/
|
||||
#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file"
|
||||
|
||||
#define MEMORY_BACKEND_FILE(obj) \
|
||||
OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE)
|
||||
|
||||
@@ -51,6 +58,29 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify pmem file size since starting a guest with an incorrect size
|
||||
* leads to confusing failures inside the guest.
|
||||
*/
|
||||
if (fb->is_pmem) {
|
||||
Error *local_err = NULL;
|
||||
uint64_t size;
|
||||
|
||||
size = qemu_get_pmem_size(fb->mem_path, &local_err);
|
||||
if (!size) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (backend->size > size) {
|
||||
error_setg(errp, "size property %" PRIu64 " is larger than "
|
||||
"pmem file \"%s\" size %" PRIu64, backend->size,
|
||||
fb->mem_path, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
name,
|
||||
@@ -110,18 +140,23 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
Error *local_err = NULL;
|
||||
uint64_t val;
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
error_setg(errp, "cannot change property '%s' of %s", name,
|
||||
object_get_typename(o));
|
||||
return;
|
||||
error_setg(&local_err, "cannot change property '%s' of %s",
|
||||
name, object_get_typename(o));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!visit_type_size(v, name, &val, errp)) {
|
||||
return;
|
||||
visit_type_size(v, name, &val, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
fb->align = val;
|
||||
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
|
||||
@@ -135,6 +170,7 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
|
||||
error_setg(errp, "cannot change property 'pmem' of %s.",
|
||||
object_get_typename(o));
|
||||
return;
|
||||
@@ -142,9 +178,13 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
|
||||
|
||||
#ifndef CONFIG_LIBPMEM
|
||||
if (value) {
|
||||
error_setg(errp, "Lack of libpmem support while setting the 'pmem=on'"
|
||||
Error *local_err = NULL;
|
||||
|
||||
error_setg(&local_err,
|
||||
"Lack of libpmem support while setting the 'pmem=on'"
|
||||
" of %s. We can't ensure data persistence.",
|
||||
object_get_typename(o));
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -174,15 +214,18 @@ file_backend_class_init(ObjectClass *oc, void *data)
|
||||
oc->unparent = file_backend_unparent;
|
||||
|
||||
object_class_property_add_bool(oc, "discard-data",
|
||||
file_memory_backend_get_discard_data, file_memory_backend_set_discard_data);
|
||||
file_memory_backend_get_discard_data, file_memory_backend_set_discard_data,
|
||||
&error_abort);
|
||||
object_class_property_add_str(oc, "mem-path",
|
||||
get_mem_path, set_mem_path);
|
||||
get_mem_path, set_mem_path,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "align", "int",
|
||||
file_memory_backend_get_align,
|
||||
file_memory_backend_set_align,
|
||||
NULL, NULL);
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "pmem",
|
||||
file_memory_backend_get_pmem, file_memory_backend_set_pmem);
|
||||
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void file_backend_instance_finalize(Object *o)
|
||||
|
||||
+23
-13
@@ -45,6 +45,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
|
||||
m->hugetlb, m->hugetlbsize, m->seal ?
|
||||
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0,
|
||||
@@ -77,22 +78,26 @@ memfd_backend_set_hugetlbsize(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj);
|
||||
Error *local_err = NULL;
|
||||
uint64_t value;
|
||||
|
||||
if (host_memory_backend_mr_inited(MEMORY_BACKEND(obj))) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
error_setg(&local_err, "cannot change property value");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!visit_type_size(v, name, &value, errp)) {
|
||||
return;
|
||||
visit_type_size(v, name, &value, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
if (!value) {
|
||||
error_setg(errp, "Property '%s.%s' doesn't take value '%" PRIu64 "'",
|
||||
object_get_typename(obj), name, value);
|
||||
return;
|
||||
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
|
||||
PRIu64 "'", object_get_typename(obj), name, value);
|
||||
goto out;
|
||||
}
|
||||
m->hugetlbsize = value;
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -137,21 +142,26 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
if (qemu_memfd_check(MFD_HUGETLB)) {
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb);
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "hugetlb",
|
||||
"Use huge pages");
|
||||
"Use huge pages",
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL);
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "hugetlbsize",
|
||||
"Huge pages size (ex: 2M, 1G)");
|
||||
"Huge pages size (ex: 2M, 1G)",
|
||||
&error_abort);
|
||||
}
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal);
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "seal",
|
||||
"Seal growing & shrinking");
|
||||
"Seal growing & shrinking",
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo memfd_backend_info = {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "qemu/module.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
|
||||
|
||||
static void
|
||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
|
||||
+45
-64
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
@@ -33,7 +32,7 @@ char *
|
||||
host_memory_backend_get_name(HostMemoryBackend *backend)
|
||||
{
|
||||
if (!backend->use_canonical_path) {
|
||||
return g_strdup(object_get_canonical_path_component(OBJECT(backend)));
|
||||
return object_get_canonical_path_component(OBJECT(backend));
|
||||
}
|
||||
|
||||
return object_get_canonical_path(OBJECT(backend));
|
||||
@@ -54,24 +53,28 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
Error *local_err = NULL;
|
||||
uint64_t value;
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
error_setg(errp, "cannot change property %s of %s ", name,
|
||||
object_get_typename(obj));
|
||||
return;
|
||||
error_setg(&local_err, "cannot change property %s of %s ",
|
||||
name, object_get_typename(obj));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!visit_type_size(v, name, &value, errp)) {
|
||||
return;
|
||||
visit_type_size(v, name, &value, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
if (!value) {
|
||||
error_setg(errp,
|
||||
error_setg(&local_err,
|
||||
"property '%s' of %s doesn't take value '%" PRIu64 "'",
|
||||
name, object_get_typename(obj), value);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
backend->size = value;
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -211,7 +214,7 @@ static bool host_memory_backend_get_prealloc(Object *obj, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
return backend->prealloc;
|
||||
return backend->prealloc || backend->force_prealloc;
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
@@ -219,6 +222,15 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
if (backend->force_prealloc) {
|
||||
if (value) {
|
||||
error_setg(errp,
|
||||
"remove -mem-prealloc to use the prealloc property");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
backend->prealloc = value;
|
||||
@@ -230,7 +242,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
void *ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
uint64_t sz = memory_region_size(&backend->mr);
|
||||
|
||||
os_mem_prealloc(fd, ptr, sz, backend->prealloc_threads, &local_err);
|
||||
os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -239,39 +251,14 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
}
|
||||
}
|
||||
|
||||
static void host_memory_backend_get_prealloc_threads(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
visit_type_uint32(v, name, &backend->prealloc_threads, errp);
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_prealloc_threads(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
uint32_t value;
|
||||
|
||||
if (!visit_type_uint32(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
if (value <= 0) {
|
||||
error_setg(errp, "property '%s' of %s doesn't take value '%d'", name,
|
||||
object_get_typename(obj), value);
|
||||
return;
|
||||
}
|
||||
backend->prealloc_threads = value;
|
||||
}
|
||||
|
||||
static void host_memory_backend_init(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
|
||||
/* TODO: convert access to globals to compat properties */
|
||||
backend->merge = machine_mem_merge(machine);
|
||||
backend->dump = machine_dump_guest_core(machine);
|
||||
backend->prealloc_threads = 1;
|
||||
backend->prealloc = mem_prealloc;
|
||||
}
|
||||
|
||||
static void host_memory_backend_post_init(Object *obj)
|
||||
@@ -316,7 +303,7 @@ size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
|
||||
#else
|
||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
|
||||
{
|
||||
return qemu_real_host_page_size;
|
||||
return getpagesize();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -325,6 +312,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
Error *local_err = NULL;
|
||||
void *ptr;
|
||||
uint64_t sz;
|
||||
@@ -374,10 +362,8 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
assert(sizeof(backend->host_nodes) >=
|
||||
BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
|
||||
assert(maxnode <= MAX_NODES);
|
||||
|
||||
if (maxnode &&
|
||||
mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 1,
|
||||
flags)) {
|
||||
if (mbind(ptr, sz, backend->policy,
|
||||
maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) {
|
||||
if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) {
|
||||
error_setg_errno(errp, errno,
|
||||
"cannot bind memory to host NUMA nodes");
|
||||
@@ -391,7 +377,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
*/
|
||||
if (backend->prealloc) {
|
||||
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
|
||||
backend->prealloc_threads, &local_err);
|
||||
ms->smp.cpus, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
@@ -456,50 +442,45 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
object_class_property_add_bool(oc, "merge",
|
||||
host_memory_backend_get_merge,
|
||||
host_memory_backend_set_merge);
|
||||
host_memory_backend_set_merge, &error_abort);
|
||||
object_class_property_set_description(oc, "merge",
|
||||
"Mark memory as mergeable");
|
||||
"Mark memory as mergeable", &error_abort);
|
||||
object_class_property_add_bool(oc, "dump",
|
||||
host_memory_backend_get_dump,
|
||||
host_memory_backend_set_dump);
|
||||
host_memory_backend_set_dump, &error_abort);
|
||||
object_class_property_set_description(oc, "dump",
|
||||
"Set to 'off' to exclude from core dump");
|
||||
"Set to 'off' to exclude from core dump", &error_abort);
|
||||
object_class_property_add_bool(oc, "prealloc",
|
||||
host_memory_backend_get_prealloc,
|
||||
host_memory_backend_set_prealloc);
|
||||
host_memory_backend_set_prealloc, &error_abort);
|
||||
object_class_property_set_description(oc, "prealloc",
|
||||
"Preallocate memory");
|
||||
object_class_property_add(oc, "prealloc-threads", "int",
|
||||
host_memory_backend_get_prealloc_threads,
|
||||
host_memory_backend_set_prealloc_threads,
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "prealloc-threads",
|
||||
"Number of CPU threads to use for prealloc");
|
||||
"Preallocate memory", &error_abort);
|
||||
object_class_property_add(oc, "size", "int",
|
||||
host_memory_backend_get_size,
|
||||
host_memory_backend_set_size,
|
||||
NULL, NULL);
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "size",
|
||||
"Size of the memory region (ex: 500M)");
|
||||
"Size of the memory region (ex: 500M)", &error_abort);
|
||||
object_class_property_add(oc, "host-nodes", "int",
|
||||
host_memory_backend_get_host_nodes,
|
||||
host_memory_backend_set_host_nodes,
|
||||
NULL, NULL);
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "host-nodes",
|
||||
"Binds memory to the list of NUMA host nodes");
|
||||
"Binds memory to the list of NUMA host nodes", &error_abort);
|
||||
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
||||
&HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy);
|
||||
host_memory_backend_set_policy, &error_abort);
|
||||
object_class_property_set_description(oc, "policy",
|
||||
"Set the NUMA policy");
|
||||
"Set the NUMA policy", &error_abort);
|
||||
object_class_property_add_bool(oc, "share",
|
||||
host_memory_backend_get_share, host_memory_backend_set_share);
|
||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "share",
|
||||
"Mark the memory as private to QEMU or shared");
|
||||
"Mark the memory as private to QEMU or shared", &error_abort);
|
||||
object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id",
|
||||
host_memory_backend_get_use_canonical_path,
|
||||
host_memory_backend_set_use_canonical_path);
|
||||
host_memory_backend_set_use_canonical_path, &error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo host_memory_backend_info = {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* QEMU Builtin Random Number Generator Backend
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/rng.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
||||
#define RNG_BUILTIN(obj) OBJECT_CHECK(RngBuiltin, (obj), TYPE_RNG_BUILTIN)
|
||||
|
||||
typedef struct RngBuiltin {
|
||||
RngBackend parent;
|
||||
QEMUBH *bh;
|
||||
} RngBuiltin;
|
||||
|
||||
static void rng_builtin_receive_entropy_bh(void *opaque)
|
||||
{
|
||||
RngBuiltin *s = opaque;
|
||||
|
||||
while (!QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||
|
||||
qemu_guest_getrandom_nofail(req->data, req->size);
|
||||
|
||||
req->receive_entropy(req->opaque, req->data, req->size);
|
||||
|
||||
rng_backend_finalize_request(&s->parent, req);
|
||||
}
|
||||
}
|
||||
|
||||
static void rng_builtin_request_entropy(RngBackend *b, RngRequest *req)
|
||||
{
|
||||
RngBuiltin *s = RNG_BUILTIN(b);
|
||||
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
|
||||
static void rng_builtin_init(Object *obj)
|
||||
{
|
||||
RngBuiltin *s = RNG_BUILTIN(obj);
|
||||
|
||||
s->bh = qemu_bh_new(rng_builtin_receive_entropy_bh, s);
|
||||
}
|
||||
|
||||
static void rng_builtin_finalize(Object *obj)
|
||||
{
|
||||
RngBuiltin *s = RNG_BUILTIN(obj);
|
||||
|
||||
qemu_bh_delete(s->bh);
|
||||
}
|
||||
|
||||
static void rng_builtin_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
rbc->request_entropy = rng_builtin_request_entropy;
|
||||
}
|
||||
|
||||
static const TypeInfo rng_builtin_info = {
|
||||
.name = TYPE_RNG_BUILTIN,
|
||||
.parent = TYPE_RNG_BACKEND,
|
||||
.instance_size = sizeof(RngBuiltin),
|
||||
.instance_init = rng_builtin_init,
|
||||
.instance_finalize = rng_builtin_finalize,
|
||||
.class_init = rng_builtin_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&rng_builtin_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
+2
-1
@@ -138,7 +138,8 @@ static char *rng_egd_get_chardev(Object *obj, Error **errp)
|
||||
static void rng_egd_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "chardev",
|
||||
rng_egd_get_chardev, rng_egd_set_chardev);
|
||||
rng_egd_get_chardev, rng_egd_set_chardev,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void rng_egd_finalize(Object *obj)
|
||||
|
||||
@@ -110,7 +110,8 @@ static void rng_random_init(Object *obj)
|
||||
|
||||
object_property_add_str(obj, "filename",
|
||||
rng_random_get_filename,
|
||||
rng_random_set_filename);
|
||||
rng_random_set_filename,
|
||||
NULL);
|
||||
|
||||
s->filename = g_strdup("/dev/urandom");
|
||||
s->fd = -1;
|
||||
|
||||
+3
-2
@@ -48,7 +48,7 @@ static bool rng_backend_prop_get_opened(Object *obj, Error **errp)
|
||||
|
||||
static void rng_backend_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(uc), "opened", true, errp);
|
||||
object_property_set_bool(OBJECT(uc), true, "opened", errp);
|
||||
}
|
||||
|
||||
static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
@@ -108,7 +108,8 @@ static void rng_backend_init(Object *obj)
|
||||
|
||||
object_property_add_bool(obj, "opened",
|
||||
rng_backend_prop_get_opened,
|
||||
rng_backend_prop_set_opened);
|
||||
rng_backend_prop_set_opened,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void rng_backend_finalize(Object *obj)
|
||||
|
||||
File renamed without changes.
@@ -1,14 +0,0 @@
|
||||
config TPM_BACKEND
|
||||
bool
|
||||
depends on TPM
|
||||
|
||||
config TPM_PASSTHROUGH
|
||||
bool
|
||||
default y
|
||||
# FIXME: should check for x86 host as well
|
||||
depends on TPM_BACKEND && LINUX
|
||||
|
||||
config TPM_EMULATOR
|
||||
bool
|
||||
default y
|
||||
depends on TPM_BACKEND
|
||||
@@ -1,4 +0,0 @@
|
||||
common-obj-y += tpm_backend.o
|
||||
common-obj-y += tpm_util.o
|
||||
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
|
||||
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
|
||||
@@ -1,33 +0,0 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# tpm_passthrough.c
|
||||
tpm_passthrough_handle_request(void *cmd) "processing command %p"
|
||||
tpm_passthrough_reset(void) "reset"
|
||||
|
||||
# tpm_util.c
|
||||
tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu"
|
||||
tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
|
||||
|
||||
# tpm_emulator.c
|
||||
tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
|
||||
tpm_emulator_handle_request(void) "processing TPM command"
|
||||
tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
|
||||
tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
|
||||
tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu"
|
||||
tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d"
|
||||
tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD"
|
||||
tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2"
|
||||
tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2"
|
||||
tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified"
|
||||
tpm_emulator_handle_device_opts_startup_error(void) "Startup error"
|
||||
tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x"
|
||||
tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x"
|
||||
tpm_emulator_set_state_blobs(void) "setting state blobs"
|
||||
tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s"
|
||||
tpm_emulator_set_state_blobs_done(void) "Done setting state blobs"
|
||||
tpm_emulator_pre_save(void) ""
|
||||
tpm_emulator_inst_init(void) ""
|
||||
@@ -1,7 +0,0 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# dbus-vmstate.c
|
||||
dbus_vmstate_pre_save(void)
|
||||
dbus_vmstate_post_load(int version_id) "version_id: %d"
|
||||
dbus_vmstate_loading(const char *id) "id: %s"
|
||||
dbus_vmstate_saving(const char *id) "id: %s"
|
||||
Loaded 100 of 4599 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user