Compare commits

..

5 Commits

Author SHA1 Message Date
Fabiano Rosas
34370ee6ff target/ppc: support single stepping with KVM HV
The hardware singlestep mechanism in POWER works via a Trace Interrupt
(0xd00) that happens after any instruction executes, whenever MSR_SE =
1 (PowerISA Section 6.5.15 - Trace Interrupt).

However, with kvm_hv, the Trace Interrupt happens inside the guest and
KVM has no visibility of it. Therefore, when the gdbstub uses the
KVM_SET_GUEST_DEBUG ioctl to enable singlestep, KVM simply ignores it.

This patch takes advantage of the Trace Interrupt to perform the step
inside the guest, but uses a breakpoint at the Trace Interrupt handler
to return control to KVM. The exit is treated by KVM as a regular
breakpoint and it returns to the host (and QEMU eventually).

Before signalling GDB, QEMU sets the Next Instruction Pointer to the
instruction following the one being stepped and restores the MSR,
SRR0, SRR1 values from before the step, effectively skipping the
interrupt handler execution and hiding the trace interrupt breakpoint
from GDB.

This approach works with both of GDB's 'scheduler-locking' options
(off, step).

Note:

- kvm_arch_set_singlestep happens after GDB asks for a single step,
  while the vcpus are stopped.

- kvm_handle_singlestep executes after the step, during the handling
  of the Emulation Assist Interrupt (breakpoint).

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
2019-02-28 16:12:49 -03:00
Fabiano Rosas
9953b32e77 target/ppc: Refactor kvm_handle_debug
There are four scenarios being handled in this function:

- single stepping
- hardware breakpoints
- software breakpoints
- fallback (no debug supported)

A future patch will add code to handle specific single step and
software breakpoints cases so let's split each scenario into its own
function now to avoid hurting readability.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>
2019-02-28 16:12:39 -03:00
Fabiano Rosas
0da2fd84e1 target/ppc: Move handling of hardware breakpoints to a separate function
This is in preparation for a refactoring of the kvm_handle_debug
function in the next patch.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
2019-02-28 16:12:37 -03:00
Fabiano Rosas
b82417e8bf kvm-all: Introduce kvm_set_singlestep
For single stepping (via KVM) of a guest vcpu to work, KVM needs not
only to support the SET_GUEST_DEBUG ioctl but to also recognize the
KVM_GUESTDBG_SINGLESTEP bit in the control field of the
kvm_guest_debug struct.

This patch adds support for querying the single step capability so
that QEMU can decide what to do for the platforms that do not have
such support.

This will allow architecture-specific implementations of a fallback
mechanism for single stepping in cases where KVM does not support it.

Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
2019-02-28 16:10:58 -03:00
Fabiano Rosas
0b72719af8 target/ppc: Move exception vector offset computation into a function
Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>
2019-02-28 16:10:29 -03:00
6004 changed files with 197180 additions and 522523 deletions

View File

@@ -1,42 +1,16 @@
env:
CIRRUS_CLONE_DEPTH: 1
freebsd_12_task: freebsd_12_task:
freebsd_instance: freebsd_instance:
image_family: freebsd-12-1 image: freebsd-12-0-release-amd64
cpu: 8 cpu: 8
memory: 8G memory: 8G
install_script: ASSUME_ALWAYS_YES=yes pkg bootstrap -f ; pkg install -y env:
bash curl cyrus-sasl git glib gmake gnutls gsed CIRRUS_CLONE_DEPTH: 1
install_script: pkg install -y
bison curl cyrus-sasl git glib gmake gnutls
nettle perl5 pixman pkgconf png usbredir nettle perl5 pixman pkgconf png usbredir
script: script:
- mkdir build - mkdir build
- cd build - cd build
- ../configure || { cat config.log; exit 1; } - ../configure || { cat config.log; exit 1; }
- gmake -j8 - 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
script:
- mkdir build
- cd build
- ../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

View File

@@ -26,15 +26,6 @@ file_type_emacs = makefile
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
[*.sh]
indent_style = space
indent_size = 4
[*.{s,S}]
indent_style = tab
indent_size = 8
file_type_emacs = asm
[*.{vert,frag}] [*.{vert,frag}]
file_type_emacs = glsl file_type_emacs = glsl

34
.github/lockdown.yml vendored
View File

@@ -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/

29
.gitignore vendored
View File

@@ -1,4 +1,3 @@
/.doctrees
/config-devices.* /config-devices.*
/config-all-devices.* /config-all-devices.*
/config-all-disas.* /config-all-disas.*
@@ -6,8 +5,6 @@
/config-target.* /config-target.*
/config.status /config.status
/config-temp /config-temp
/tools/virtiofsd/50-qemu-virtiofsd.json
/elf2dmp
/trace-events-all /trace-events-all
/trace/generated-events.h /trace/generated-events.h
/trace/generated-events.c /trace/generated-events.c
@@ -34,18 +31,19 @@
/qapi/qapi-builtin-types.[ch] /qapi/qapi-builtin-types.[ch]
/qapi/qapi-builtin-visit.[ch] /qapi/qapi-builtin-visit.[ch]
/qapi/qapi-commands-*.[ch] /qapi/qapi-commands-*.[ch]
**/qapi/qapi-commands.[ch] /qapi/qapi-commands.[ch]
**/qapi/qapi-emit-events.[ch] /qapi/qapi-emit-events.[ch]
/qapi/qapi-events-*.[ch] /qapi/qapi-events-*.[ch]
**/qapi/qapi-events.[ch] /qapi/qapi-events.[ch]
**/qapi/qapi-init-commands.[ch] /qapi/qapi-introspect.[ch]
**/qapi/qapi-introspect.[ch]
/qapi/qapi-types-*.[ch] /qapi/qapi-types-*.[ch]
**/qapi/qapi-types.[ch] /qapi/qapi-types.[ch]
/qapi/qapi-visit-*.[ch] /qapi/qapi-visit-*.[ch]
!/qapi/qapi-visit-core.c /qapi/qapi-visit.[ch]
**/qapi/qapi-visit.[ch] /qapi/qapi-doc.texi
**/qapi/qapi-doc.texi /qemu-doc.html
/qemu-doc.info
/qemu-doc.txt
/qemu-edid /qemu-edid
/qemu-img /qemu-img
/qemu-nbd /qemu-nbd
@@ -59,15 +57,12 @@
/qemu-keymap /qemu-keymap
/qemu-monitor.texi /qemu-monitor.texi
/qemu-monitor-info.texi /qemu-monitor-info.texi
/qemu-storage-daemon
/qemu-version.h /qemu-version.h
/qemu-version.h.tmp /qemu-version.h.tmp
/module_block.h /module_block.h
/scsi/qemu-pr-helper /scsi/qemu-pr-helper
/vhost-user-scsi /vhost-user-scsi
/vhost-user-blk /vhost-user-blk
/vhost-user-gpu
/vhost-user-input
/fsdev/virtfs-proxy-helper /fsdev/virtfs-proxy-helper
*.tmp *.tmp
*.[1-9] *.[1-9]
@@ -93,7 +88,6 @@
*.tp *.tp
*.vr *.vr
*.d *.d
!/.gitlab-ci.d
!/scripts/qemu-guest-agent/fsfreeze-hook.d !/scripts/qemu-guest-agent/fsfreeze-hook.d
*.o *.o
.sdk .sdk
@@ -101,7 +95,6 @@
*.gcno *.gcno
*.gcov *.gcov
/pc-bios/bios-pq/status /pc-bios/bios-pq/status
/pc-bios/edk2-*.fd
/pc-bios/vgabios-pq/status /pc-bios/vgabios-pq/status
/pc-bios/optionrom/linuxboot.asm /pc-bios/optionrom/linuxboot.asm
/pc-bios/optionrom/linuxboot.bin /pc-bios/optionrom/linuxboot.bin
@@ -125,7 +118,6 @@
/pc-bios/optionrom/kvmvapic.img /pc-bios/optionrom/kvmvapic.img
/pc-bios/s390-ccw/s390-ccw.elf /pc-bios/s390-ccw/s390-ccw.elf
/pc-bios/s390-ccw/s390-ccw.img /pc-bios/s390-ccw/s390-ccw.img
/docs/built
/docs/interop/qemu-ga-qapi.texi /docs/interop/qemu-ga-qapi.texi
/docs/interop/qemu-ga-ref.html /docs/interop/qemu-ga-ref.html
/docs/interop/qemu-ga-ref.info* /docs/interop/qemu-ga-ref.info*
@@ -135,7 +127,6 @@
/docs/interop/qemu-qmp-ref.info* /docs/interop/qemu-qmp-ref.info*
/docs/interop/qemu-qmp-ref.txt /docs/interop/qemu-qmp-ref.txt
/docs/version.texi /docs/version.texi
/contrib/vhost-user-gpu/50-qemu-gpu.json
*.tps *.tps
.stgit-* .stgit-*
.git-submodule-status .git-submodule-status

View File

@@ -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

View File

@@ -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

View File

@@ -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/*

View File

@@ -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

View File

@@ -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}"

View File

@@ -1,202 +1,73 @@
# Currently we have two build stages after our containers are built: before_script:
# - build (for traditional build and test or first stage build) - apt-get update -qq
# - test (for test stages, using build artefacts from a build stage) - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
stages:
- containers
- containers-layer2
- containers-layer3
- build
- test
# We assume GitLab has it's own caching set up for RPM/APT repositories so we build-system1:
# just take care of avocado assets here. script:
cache: - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
paths: libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
- $HOME/avocado/data/cache - ./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: build-system2:
- local: '/.gitlab-ci.d/edk2.yml' script:
- local: '/.gitlab-ci.d/opensbi.yml' - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
- local: '/.gitlab-ci.d/containers.yml' libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
- ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
.native_build_job_template: &native_build_job_definition microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
stage: build sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest - make -j2
before_script: - make -j2 check
- 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
.post_acceptance_template: &post_acceptance
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 $HOME/avocado/data/cache
build-system-ubuntu-main:
<<: *native_build_job_definition
variables:
IMAGE: ubuntu2004
TARGETS: 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_CHECK_ARGS: check-build
artifacts:
paths:
- build
check-system-ubuntu-main:
<<: *native_test_job_definition
needs:
- job: build-system-ubuntu-main
artifacts: true
variables:
IMAGE: ubuntu2004
MAKE_CHECK_ARGS: check
acceptance-system-ubuntu-main:
<<: *native_test_job_definition
needs:
- job: build-system-ubuntu-main
artifacts: true
variables:
IMAGE: ubuntu2004
MAKE_CHECK_ARGS: check-acceptance
<<: *post_acceptance
build-system-fedora-alt:
<<: *native_build_job_definition
variables:
IMAGE: fedora
TARGETS: 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_CHECK_ARGS: check-build
artifacts:
paths:
- build
check-system-fedora-alt:
<<: *native_test_job_definition
needs:
- job: build-system-fedora-alt
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check
acceptance-system-fedora-alt:
<<: *native_test_job_definition
needs:
- job: build-system-fedora-alt
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-acceptance
<<: *post_acceptance
build-disabled: build-disabled:
<<: *native_build_job_definition script:
variables: - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
IMAGE: fedora
CONFIGURE_ARGS: --disable-rdma --disable-slirp --disable-curl
--disable-capstone --disable-live-block-migration --disable-glusterfs --disable-capstone --disable-live-block-migration --disable-glusterfs
--disable-replication --disable-coroutine-pool --disable-smartcard --disable-replication --disable-coroutine-pool --disable-smartcard
--disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
--disable-qom-cast-debug --disable-spice --disable-vhost-vsock --disable-qom-cast-debug --disable-spice --disable-vhost-vsock
--disable-vhost-net --disable-vhost-crypto --disable-vhost-user --disable-vhost-net --disable-vhost-crypto --disable-vhost-user
TARGETS: i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
MAKE_CHECK_ARGS: check-qtest SPEED=slow - make -j2
- make -j2 check-qtest SPEED=slow
build-tcg-disabled: build-tcg-disabled:
<<: *native_build_job_definition script:
variables: - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
IMAGE: centos8 - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
script: - make -j2
- mkdir build - make check-unit
- cd build - make check-qapi-schema
- ../configure --disable-tcg --audio-drv-list="" - cd tests/qemu-iotests/
- make -j"$JOBS" - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
- make check-unit 052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
- make check-qapi-schema 163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
- cd tests/qemu-iotests/ - ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163 039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277 060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
- ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122 085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202 110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258 139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
260 261 262 263 264 270 272 273 277 279 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: build-user:
<<: *native_build_job_definition script:
variables: - ./configure --enable-werror --disable-system --disable-guest-agent
IMAGE: debian-all-test-cross --disable-capstone --disable-slirp --disable-fdt
CONFIGURE_ARGS: --disable-tools --disable-system - make -j2
MAKE_CHECK_ARGS: check-tcg - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
build-clang: build-clang:
<<: *native_build_job_definition script:
variables: - apt-get install -y -qq clang libsdl2-dev
IMAGE: fedora xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
CONFIGURE_ARGS: --cc=clang --cxx=clang++ - ./configure --cc=clang --cxx=clang++ --enable-werror
TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
ppc-softmmu s390x-softmmu arm-linux-user ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
MAKE_CHECK_ARGS: check - make -j2
- make -j2 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
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

21
.gitmodules vendored
View File

@@ -10,6 +10,9 @@
[submodule "roms/openbios"] [submodule "roms/openbios"]
path = roms/openbios path = roms/openbios
url = https://git.qemu.org/git/openbios.git 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"] [submodule "roms/qemu-palcode"]
path = roms/qemu-palcode path = roms/qemu-palcode
url = https://git.qemu.org/git/qemu-palcode.git url = https://git.qemu.org/git/qemu-palcode.git
@@ -36,25 +39,13 @@
url = https://git.qemu.org/git/capstone.git url = https://git.qemu.org/git/capstone.git
[submodule "roms/seabios-hppa"] [submodule "roms/seabios-hppa"]
path = roms/seabios-hppa path = roms/seabios-hppa
url = https://git.qemu.org/git/seabios-hppa.git url = https://github.com/hdeller/seabios-hppa.git
[submodule "roms/u-boot-sam460ex"] [submodule "roms/u-boot-sam460ex"]
path = roms/u-boot-sam460ex path = roms/u-boot-sam460ex
url = https://git.qemu.org/git/u-boot-sam460ex.git url = https://git.qemu.org/git/u-boot-sam460ex.git
[submodule "tests/fp/berkeley-testfloat-3"] [submodule "tests/fp/berkeley-testfloat-3"]
path = tests/fp/berkeley-testfloat-3 path = tests/fp/berkeley-testfloat-3
url = https://git.qemu.org/git/berkeley-testfloat-3.git url = https://github.com/cota/berkeley-testfloat-3
[submodule "tests/fp/berkeley-softfloat-3"] [submodule "tests/fp/berkeley-softfloat-3"]
path = tests/fp/berkeley-softfloat-3 path = tests/fp/berkeley-softfloat-3
url = https://git.qemu.org/git/berkeley-softfloat-3.git url = https://github.com/cota/berkeley-softfloat-3
[submodule "roms/edk2"]
path = roms/edk2
url = https://git.qemu.org/git/edk2.git
[submodule "slirp"]
path = slirp
url = https://git.qemu.org/git/libslirp.git
[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

147
.mailmap
View File

@@ -1,29 +1,23 @@
# This mailmap fixes up author names/addresses. # 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 # The first section translates weird addresses from the original git import
# into proper addresses so that they are counted properly by git shortlog. # into proper addresses so that they are counted properly by git shortlog.
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> 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> 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> Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@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> 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> 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> 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> 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> Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@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> 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> 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 # Also list preferred name forms where people have changed their
# git author config, or had utf8/latin1 encoding issues. # 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> 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> 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>

View File

@@ -1,302 +0,0 @@
---
# Note: this file is still unused. It serves as a documentation for the
# Patchew configuration in case patchew.org disappears or has to be
# reinstalled.
#
# Patchew configuration is available to project administrators at
# https://patchew.org/api/v1/projects/1/config/ and can be configured
# to YAML using the following Python script:
#
# import json
# import sys
# import ruamel.yaml
#
# json_str = sys.stdin.read()
# yaml = ruamel.yaml.YAML()
# yaml.explicit_start = True
# data = json.loads(json_str, object_pairs_hook=ruamel.yaml.comments.CommentedMap)
# ruamel.yaml.scalarstring.walk_tree(data)
# yaml.dump(data, sys.stdout)
email:
notifications:
timeouts:
event: TestingReport
enabled: true
to_user: false
reply_subject: true
set_reply_to: true
in_reply_to: true
reply_to_all: false
subject_template: none
to: fam@euphon.net
cc: ''
body_template: |
{% if not is_timeout %} {{ cancel }} {% endif %}
Test '{{ test }}' timeout, log:
{{ log }}
ENOSPC:
event: TestingReport
enabled: true
to_user: false
reply_subject: false
set_reply_to: false
in_reply_to: true
reply_to_all: false
subject_template: Out of space error
to: fam@euphon.net
cc: ''
body_template: |
{% if passed %}
{{ cancel }}
{% endif %}
{% if 'No space left on device' in log %}
Tester {{ tester }} out of space when running {{ test }}
{{ log }}
{% else %}
{{ cancel }}
{% endif %}
FailureShort:
event: TestingReport
enabled: true
to_user: false
reply_subject: true
set_reply_to: true
in_reply_to: true
reply_to_all: true
subject_template: Testing failed
to: ''
cc: ''
body_template: |
{% if passed or not obj.message_id or is_timeout %}
{{ cancel }}
{% endif %}
{% if 'No space left on device' in log %}
{{ cancel }}
{% endif %}
Patchew URL: https://patchew.org/QEMU/{{ obj.message_id }}/
{% ansi2text log as logtext %}
{% if test == "checkpatch" %}
Hi,
This series seems to have some coding style problems. See output below for
more information:
{{ logtext }}
{% elif test == "docker-mingw@fedora" or test == "docker-quick@centos7" or test == "asan" %}
Hi,
This series failed the {{ test }} build test. Please find the testing commands and
their output below. If you have Docker installed, you can probably reproduce it
locally.
{% lines_between logtext start="^=== TEST SCRIPT BEGIN ===$" stop="^=== TEST SCRIPT END ===$" %}
{% lines_between logtext start="^=== OUTPUT BEGIN ===$" stop="=== OUTPUT END ===$" as output %}
{% grep_C output regex="\b(FAIL|XPASS|ERROR|WARN|error:|warning:)" n=3 %}
{% elif test == "s390x" or test == "FreeBSD" or test == "ppcle" or test == "ppcbe" %}
Hi,
This series failed build test on {{test}} host. Please find the details below.
{% lines_between logtext start="^=== TEST SCRIPT BEGIN ===$" stop="^=== TEST SCRIPT END ===$" %}
{% lines_between logtext start="^=== OUTPUT BEGIN ===$" stop="=== OUTPUT END ===$" as output %}
{% grep_C output regex="\b(FAIL|XPASS|ERROR|WARN|error:|warning:)" n=3 %}
{% else %}
{{ cancel }}
{% endif %}
The full log is available at
{{ log_url }}.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
testing:
tests:
asan:
enabled: true
requirements: docker
timeout: 3600
script: |
#!/bin/bash
time make docker-test-debug@fedora TARGET_LIST=x86_64-softmmu J=14 NETWORK=1
docker-quick@centos7:
enabled: false
requirements: docker,x86_64
timeout: 3600
script: |
#!/bin/bash
time make docker-test-quick@centos7 SHOW_ENV=1 J=14 NETWORK=1
checkpatch:
enabled: true
requirements: ''
timeout: 600
script: |
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
docker-mingw@fedora:
enabled: true
requirements: docker,x86_64
timeout: 3600
script: |
#! /bin/bash
test "$(uname -m)" = "x86_64"
ppcle:
enabled: false
requirements: ppcle
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
set -e
CC=$HOME/bin/cc
INSTALL=$PWD/install
BUILD=$PWD/build
mkdir -p $BUILD $INSTALL
SRC=$PWD
cd $BUILD
$SRC/configure --cc=$CC --prefix=$INSTALL
make -j4
# XXX: we need reliable clean up
# make check -j4 V=1
make install
echo
echo "=== ENV ==="
env
echo
echo "=== PACKAGES ==="
rpm -qa
ppcbe:
enabled: false
requirements: ppcbe
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
set -e
CC=$HOME/bin/cc
INSTALL=$PWD/install
BUILD=$PWD/build
mkdir -p $BUILD $INSTALL
SRC=$PWD
cd $BUILD
$SRC/configure --cc=$CC --prefix=$INSTALL
make -j4
# XXX: we need reliable clean up
# make check -j4 V=1
make install
echo
echo "=== ENV ==="
env
echo
echo "=== PACKAGES ==="
rpm -qa
FreeBSD:
enabled: true
requirements: qemu-x86,x86_64,git
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
if qemu-system-x86_64 --help >/dev/null 2>&1; then
QEMU=qemu-system-x86_64
elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then
QEMU=/usr/libexec/qemu-kvm
else
exit 1
fi
make vm-build-freebsd J=21 QEMU=$QEMU
exit 0
docker-clang@ubuntu:
enabled: true
requirements: docker,x86_64
timeout: 3600
script: |
#!/bin/bash
time make docker-test-clang@ubuntu SHOW_ENV=1 J=14 NETWORK=1
s390x:
enabled: true
requirements: s390x
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
set -e
CC=$HOME/bin/cc
INSTALL=$PWD/install
BUILD=$PWD/build
mkdir -p $BUILD $INSTALL
SRC=$PWD
cd $BUILD
$SRC/configure --cc=$CC --prefix=$INSTALL
make -j4
# XXX: we need reliable clean up
# make check -j4 V=1
make install
echo
echo "=== ENV ==="
env
echo
echo "=== PACKAGES ==="
rpm -qa
requirements:
x86_64:
script: |
#! /bin/bash
test "$(uname -m)" = "x86_64"
qemu-x86:
script: |
#!/bin/bash
if qemu-system-x86_64 --help >/dev/null 2>&1; then
:
elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then
:
else
exit 1
fi
ppcle:
script: |
#!/bin/bash
test "$(uname -m)" = "ppc64le"
ppcbe:
script: |
#!/bin/bash
test "$(uname -m)" = "ppc64"
git:
script: |
#! /bin/bash
git config user.name > /dev/null 2>&1
docker:
script: |
#!/bin/bash
docker ps || sudo -n docker ps
s390x:
script: |
#!/bin/bash
test "$(uname -m)" = "s390x"
git:
push_to: git@github.com:patchew-project/qemu
public_repo: https://github.com/patchew-project/qemu
url_template: https://github.com/patchew-project/qemu/tree/%t

View File

@@ -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

View File

@@ -7,10 +7,11 @@ env:
matrix: matrix:
- IMAGE=debian-amd64 - IMAGE=debian-amd64
TARGET_LIST=x86_64-softmmu,x86_64-linux-user TARGET_LIST=x86_64-softmmu,x86_64-linux-user
- IMAGE=debian-win32-cross # currently disabled as the mxe.cc repos are down
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu # - IMAGE=debian-win32-cross
- IMAGE=debian-win64-cross # TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu # - IMAGE=debian-win64-cross
# TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
- IMAGE=debian-armel-cross - IMAGE=debian-armel-cross
TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
- IMAGE=debian-armhf-cross - IMAGE=debian-armhf-cross
@@ -26,14 +27,14 @@ env:
- IMAGE=debian-ppc64el-cross - IMAGE=debian-ppc64el-cross
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
build: build:
pre_ci:
- make docker-image-${IMAGE} V=1
pre_ci_boot: pre_ci_boot:
image_name: registry.gitlab.com/qemu-project/qemu/qemu/${IMAGE} image_name: qemu
image_tag: latest image_tag: ${IMAGE}
pull: true pull: false
options: "-e HOME=/root" options: "-e HOME=/root"
ci: ci:
- unset CC - unset CC
- mkdir build - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- cd build
- ../configure --disable-docs ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) - make -j$(($(getconf _NPROCESSORS_ONLN) + 1))

View File

@@ -1,22 +1,11 @@
# The current Travis default is a VM based 16.04 Xenial on GCE # The current Travis default is a VM based 16.04 Xenial on GCE
# Additional builds with specific requirements for a full VM need to # Additional builds with specific requirements for a full VM need to
# be added as additional matrix: entries later on # be added as additional matrix: entries later on
os: linux
dist: xenial dist: xenial
language: c language: c
compiler: compiler:
- gcc - gcc
cache: cache: ccache
# 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
addons: addons:
@@ -28,7 +17,7 @@ addons:
- libbrlapi-dev - libbrlapi-dev
- libcap-ng-dev - libcap-ng-dev
- libgcc-4.8-dev - libgcc-4.8-dev
- libgnutls28-dev - libgnutls-dev
- libgtk-3-dev - libgtk-3-dev
- libiscsi-dev - libiscsi-dev
- liblttng-ust-dev - liblttng-ust-dev
@@ -36,24 +25,23 @@ addons:
- libnfs-dev - libnfs-dev
- libnss3-dev - libnss3-dev
- libpixman-1-dev - libpixman-1-dev
- libpng-dev - libpng12-dev
- librados-dev - librados-dev
- libsdl2-dev - libsdl1.2-dev
- libsdl2-image-dev
- libseccomp-dev - libseccomp-dev
- libspice-protocol-dev - libspice-protocol-dev
- libspice-server-dev - libspice-server-dev
- libssh-dev - libssh2-1-dev
- liburcu-dev - liburcu-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev - libvte-2.91-dev
- libzstd-dev
- sparse - sparse
- uuid-dev - uuid-dev
- gcovr - gcovr
# Tests dependencies homebrew:
- genisoimage packages:
- glib
- pixman
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
@@ -69,229 +57,154 @@ notifications:
env: env:
global: global:
- SRC_DIR=".." - SRC_DIR="."
- BUILD_DIR="build" - BUILD_DIR="."
- BASE_CONFIG="--disable-docs --disable-tools" - BASE_CONFIG="--disable-docs --disable-tools"
- TEST_BUILD_CMD="" - TEST_CMD="make check -j3 V=1"
- TEST_CMD="make check 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
git: git:
# we want to do this ourselves # we want to do this ourselves
submodules: false 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: before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
# Main build & test - rarely overridden - controlled by TEST_CMD
script: script:
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? - make -j3 && ${TEST_CMD}
- |
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
jobs: matrix:
include: include:
- name: "GCC static (user)" - env:
env: - CONFIG="--disable-system"
- 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 - env:
- name: "GCC (main-softmmu)" - CONFIG="--disable-user"
env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- name: "GCC (other-softmmu)" - env:
env: - CONFIG="--enable-debug --enable-debug-tcg --disable-user"
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
# Just build tools and run minimal unit and softfloat checks # TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
- 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"
# --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:
- CONFIG="--enable-debug-tcg --disable-system" - 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-user --disable-replication"
- 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 - env:
- name: "GCC modules (main-softmmu)" - CONFIG="--enable-modules --disable-linux-user"
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 # 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 # 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" - 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" - 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) # Check we can build docs and tools
- name: "tools and docs (bionic)" - env:
dist: bionic
env:
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
- BASE_CONFIG="--enable-tools --enable-docs" - BASE_CONFIG="--enable-tools --enable-docs"
- CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user" - CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
addons: addons:
apt: apt:
packages: packages:
- python3-sphinx - python-sphinx
- texinfo - texinfo
- perl - perl
# Test out-of-tree builds
- env:
- CONFIG="--enable-debug --enable-debug-tcg"
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
# Test with Clang for compile portability (Travis uses clang-5.0) # Test with Clang for compile portability (Travis uses clang-5.0)
- name: "Clang (user)" - env:
env: - CONFIG="--disable-system"
- CONFIG="--disable-system --host-cc=clang --cxx=clang++"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
compiler: clang compiler: clang
- name: "Clang (main-softmmu)" - env:
env: - CONFIG="--disable-user"
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}
--host-cc=clang --cxx=clang++"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize"
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; }
- 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"
compiler: clang compiler: clang
# gprof/gcov are GCC features # gprof/gcov are GCC features
- name: "GCC gprof/gcov" - env:
env: - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
after_success: after_success:
- ${SRC_DIR}/scripts/travis/coverage-summary.sh - ${SRC_DIR}/scripts/travis/coverage-summary.sh
# We manually include builds which we disable "make check" for # We manually include builds which we disable "make check" for
- name: "GCC without-default-devices (softmmu)" - env:
env: - CONFIG="--enable-debug --enable-tcg-interpreter"
- CONFIG="--without-default-devices --disable-user"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- TEST_CMD="" - 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 don't need to exercise every backend with every front-end # 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" - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
- TEST_CMD="" - TEST_CMD=""
- name: "GCC trace ftrace (x86_64-softmmu)" - env:
env:
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
- TEST_CMD="" - TEST_CMD=""
- name: "GCC trace ust (x86_64-softmmu)" - env:
env:
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
- TEST_CMD="" - TEST_CMD=""
# MacOSX builds
- env:
- CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
os: osx
osx_image: xcode9.4
compiler: clang
# Python builds # Python builds
- name: "GCC Python 3.5 (x86_64-softmmu)" - env:
env:
- CONFIG="--target-list=x86_64-softmmu" - CONFIG="--target-list=x86_64-softmmu"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
language: python language: python
python: 3.5 python:
- "3.4"
- name: "GCC Python 3.6 (x86_64-softmmu)" - env:
env:
- CONFIG="--target-list=x86_64-softmmu" - CONFIG="--target-list=x86_64-softmmu"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
language: python language: python
python: 3.6 python:
- "3.6"
# Using newer GCC with sanitizers # Acceptance (Functional) tests
- name: "GCC9 with sanitizers (softmmu)" - env:
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
- TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
addons: addons:
apt:
packages:
- python3-pip
- python3.5-venv
# Using newer GCC with sanitizers
- addons:
apt: apt:
update: true update: true
sources: sources:
@@ -299,8 +212,8 @@ jobs:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
# Extra toolchains # Extra toolchains
- gcc-9 - gcc-7
- g++-9 - g++-7
# Build dependencies # Build dependencies
- libaio-dev - libaio-dev
- libattr1-dev - libattr1-dev
@@ -316,12 +229,11 @@ jobs:
- libpixman-1-dev - libpixman-1-dev
- libpng12-dev - libpng12-dev
- librados-dev - librados-dev
- libsdl2-dev - libsdl1.2-dev
- libsdl2-image-dev
- libseccomp-dev - libseccomp-dev
- libspice-protocol-dev - libspice-protocol-dev
- libspice-server-dev - libspice-server-dev
- libssh-dev - libssh2-1-dev
- liburcu-dev - liburcu-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- libvte-2.91-dev - libvte-2.91-dev
@@ -330,250 +242,13 @@ jobs:
language: generic language: generic
compiler: none compiler: none
env: env:
- COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9 - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user" - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user"
- TEST_CMD="" - TEST_CMD=""
before_script: before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread" --extra-ldflags="-fuse-ld=gold" || { cat config.log && exit 1; }
# Run check-tcg against linux-user - env:
- name: "GCC check-tcg (user)" - CONFIG="--disable-system"
env: - TEST_CMD="make -j3 check-tcg V=1"
- 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"
# 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

177
CODING_STYLE Normal file
View File

@@ -0,0 +1,177 @@
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.
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

View File

@@ -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

245
HACKING Normal file
View File

@@ -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().

View File

@@ -1,4 +0,0 @@
source Kconfig.host
source backends/Kconfig
source accel/Kconfig
source hw/Kconfig

View File

@@ -1,35 +0,0 @@
# These are "proxy" symbols used to pass config-host.mak values
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
# these two need to be kept in sync.
config LINUX
bool
config OPENGL
bool
config X11
bool
config SPICE
bool
config IVSHMEM
bool
config TPM
bool
config VHOST_USER
bool
select VHOST
config VHOST_KERNEL
bool
select VHOST
config VIRTFS
bool
config PVRDMA
bool

26
LICENSE
View File

@@ -1,26 +1,20 @@
The QEMU distribution includes both the QEMU emulator and The following points clarify the QEMU license:
various firmware files. These are separate programs that are
distributed together for our users' convenience, and they have
separate licenses.
The following points clarify the license of the QEMU emulator: 1) QEMU as a whole is released under the GNU General Public License,
version 2.
1) The QEMU emulator as a whole is released under the GNU General 2) Parts of QEMU have specific licenses which are compatible with the
Public License, version 2. GNU General Public License, version 2. Hence each source file contains
its own licensing information. Source files with no licensing information
2) Parts of the QEMU emulator have specific licenses which are compatible are released under the GNU General Public License, version 2 or (at your
with the GNU General Public License, version 2. Hence each source file option) any later version.
contains its own licensing information. Source files with no licensing
information are released under the GNU General Public License, version
2 or (at your option) any later version.
As of July 2013, contributions under version 2 of the GNU General Public 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 License (and no later version) are only accepted for the following files
or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*. 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; 3) The Tiny Code Generator (TCG) is released under the BSD license
but some parts may be GPLv2 or other licenses. Again, see the (see license headers in files).
specific licensing information in each source file.
4) QEMU is a trademark of Fabrice Bellard. 4) QEMU is a trademark of Fabrice Bellard.

File diff suppressed because it is too large Load Diff

675
Makefile

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,42 @@
####################################################################### #######################################################################
# Common libraries for tools and emulators # Common libraries for tools and emulators
stub-obj-y = stubs/ stub-obj-y = stubs/ util/ crypto/
util-obj-y = crypto/ util/ qobject/ qapi/ util-obj-y = 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)
chardev-obj-y = chardev/ chardev-obj-y = chardev/
slirp-obj-$(CONFIG_SLIRP) = slirp/
#######################################################################
# authz-obj-y is code used by both qemu system emulation and qemu-img
authz-obj-y = authz/ 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.o blockjob.o job.o
block-obj-y += block/ scsi/
block-obj-y += qemu-io-cmds.o block-obj-y += qemu-io-cmds.o
block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-m = block/ block-obj-m = block/
#######################################################################
# crypto-obj-y is code used by both qemu system emulation and qemu-img
crypto-obj-y = crypto/ crypto-obj-y = crypto/
crypto-aes-obj-y = crypto/
io-obj-y = io/
endif # CONFIG_SOFTMMU or CONFIG_TOOLS
####################################################################### #######################################################################
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are # qom-obj-y is code used by both qemu system emulation and qemu-img
# used for system emulation, too, but specified separately there)
storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/ qom-obj-y = qom/
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 # 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 # Target independent part of system emulation. The long term path is to
@@ -43,35 +46,34 @@ storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
ifeq ($(CONFIG_SOFTMMU),y) ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o common-obj-y += bootdevice.o iothread.o
common-obj-y += dump/
common-obj-y += job-qmp.o common-obj-y += job-qmp.o
common-obj-y += monitor/
common-obj-y += net/ 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_WIN32) += os-win32.o
common-obj-$(CONFIG_POSIX) += os-posix.o common-obj-$(CONFIG_POSIX) += os-posix.o
common-obj-$(CONFIG_LINUX) += fsdev/ common-obj-$(CONFIG_LINUX) += fsdev/
common-obj-y += accel/
common-obj-y += migration/ common-obj-y += migration/
common-obj-y += audio/ common-obj-y += audio/
common-obj-m += audio/ common-obj-m += audio/
common-obj-y += hw/ common-obj-y += hw/
common-obj-m += hw/
common-obj-y += replay/ common-obj-y += replay/
common-obj-y += ui/ common-obj-y += ui/
common-obj-m += 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 += dma-helpers.o
common-obj-y += vl.o
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
common-obj-$(CONFIG_TPM) += tpm.o common-obj-$(CONFIG_TPM) += tpm.o
common-obj-y += backends/ common-obj-y += backends/
common-obj-y += chardev/ common-obj-y += chardev/
common-obj-m += chardev/
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS) qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS)
@@ -79,9 +81,12 @@ qemu-seccomp.o-libs := $(SECCOMP_LIBS)
common-obj-$(CONFIG_FDT) += device_tree.o common-obj-$(CONFIG_FDT) += device_tree.o
common-obj-y += qapi/ ######################################################################
# qapi
endif # CONFIG_SOFTMMU common-obj-y += qmp.o hmp.o
common-obj-y += qapi/
endif
####################################################################### #######################################################################
# Target-independent parts used in system and user emulation # Target-independent parts used in system and user emulation
@@ -97,6 +102,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
###################################################################### ######################################################################
# tracing # tracing
util-obj-y += trace/ util-obj-y += trace/
target-obj-y += trace/
###################################################################### ######################################################################
# guest agent # guest agent
@@ -118,31 +124,16 @@ vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
vhost-user-blk-obj-y = contrib/vhost-user-blk/ vhost-user-blk-obj-y = contrib/vhost-user-blk/
rdmacm-mux-obj-y = contrib/rdmacm-mux/ 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 =
trace-events-subdirs += accel/kvm trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg trace-events-subdirs += accel/tcg
trace-events-subdirs += backends trace-events-subdirs += audio
trace-events-subdirs += backends/tpm
trace-events-subdirs += crypto
trace-events-subdirs += monitor
ifeq ($(CONFIG_USER_ONLY),y)
trace-events-subdirs += linux-user
endif
ifeq ($(CONFIG_BLOCK),y)
trace-events-subdirs += authz trace-events-subdirs += authz
trace-events-subdirs += block trace-events-subdirs += block
trace-events-subdirs += io
trace-events-subdirs += nbd
trace-events-subdirs += scsi
endif
ifeq ($(CONFIG_SOFTMMU),y)
trace-events-subdirs += audio
trace-events-subdirs += chardev trace-events-subdirs += chardev
trace-events-subdirs += crypto
trace-events-subdirs += hw/9pfs trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/acpi trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/alpha trace-events-subdirs += hw/alpha
@@ -151,9 +142,9 @@ trace-events-subdirs += hw/audio
trace-events-subdirs += hw/block trace-events-subdirs += hw/block
trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/block/dataplane
trace-events-subdirs += hw/char trace-events-subdirs += hw/char
trace-events-subdirs += hw/display
trace-events-subdirs += hw/dma trace-events-subdirs += hw/dma
trace-events-subdirs += hw/hppa trace-events-subdirs += hw/hppa
trace-events-subdirs += hw/hyperv
trace-events-subdirs += hw/i2c trace-events-subdirs += hw/i2c
trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386
trace-events-subdirs += hw/i386/xen trace-events-subdirs += hw/i386/xen
@@ -162,7 +153,6 @@ trace-events-subdirs += hw/input
trace-events-subdirs += hw/intc trace-events-subdirs += hw/intc
trace-events-subdirs += hw/isa trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem trace-events-subdirs += hw/mem
trace-events-subdirs += hw/mips
trace-events-subdirs += hw/misc trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/net trace-events-subdirs += hw/net
@@ -172,13 +162,11 @@ trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/ppc trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/rdma trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/rtc
trace-events-subdirs += hw/s390x trace-events-subdirs += hw/s390x
trace-events-subdirs += hw/scsi trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/sd trace-events-subdirs += hw/sd
trace-events-subdirs += hw/sparc trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sparc64 trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/ssi
trace-events-subdirs += hw/timer trace-events-subdirs += hw/timer
trace-events-subdirs += hw/tpm trace-events-subdirs += hw/tpm
trace-events-subdirs += hw/usb trace-events-subdirs += hw/usb
@@ -187,23 +175,21 @@ trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/watchdog trace-events-subdirs += hw/watchdog
trace-events-subdirs += hw/xen trace-events-subdirs += hw/xen
trace-events-subdirs += hw/gpio trace-events-subdirs += hw/gpio
trace-events-subdirs += hw/riscv trace-events-subdirs += io
trace-events-subdirs += linux-user
trace-events-subdirs += migration trace-events-subdirs += migration
trace-events-subdirs += nbd
trace-events-subdirs += net 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 += qapi
trace-events-subdirs += qom trace-events-subdirs += qom
trace-events-subdirs += scsi
trace-events-subdirs += target/arm trace-events-subdirs += target/arm
trace-events-subdirs += target/hppa
trace-events-subdirs += target/i386 trace-events-subdirs += target/i386
trace-events-subdirs += target/mips trace-events-subdirs += target/mips
trace-events-subdirs += target/ppc trace-events-subdirs += target/ppc
trace-events-subdirs += target/riscv
trace-events-subdirs += target/s390x trace-events-subdirs += target/s390x
trace-events-subdirs += target/sparc trace-events-subdirs += target/sparc
trace-events-subdirs += ui
trace-events-subdirs += util trace-events-subdirs += util
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)

View File

@@ -4,15 +4,12 @@ BUILD_DIR?=$(CURDIR)/..
include ../config-host.mak include ../config-host.mak
include config-target.mak include config-target.mak
include $(SRC_PATH)/rules.mak
ifdef CONFIG_SOFTMMU
include config-devices.mak include config-devices.mak
endif include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) $(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
ifdef CONFIG_LINUX ifdef CONFIG_LINUX
QEMU_CFLAGS += -isystem ../linux-headers QEMU_CFLAGS += -I../linux-headers
endif endif
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
@@ -39,12 +36,14 @@ endif
PROGS=$(QEMU_PROG) $(QEMU_PROGW) PROGS=$(QEMU_PROG) $(QEMU_PROGW)
STPFILES= STPFILES=
# Makefile Tests
ifdef CONFIG_USER_ONLY
include $(SRC_PATH)/tests/tcg/Makefile.include
endif
config-target.h: config-target.h-timestamp config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak config-target.h-timestamp: config-target.mak
config-devices.h: config-devices.h-timestamp
config-devices.h-timestamp: config-devices.mak
ifdef CONFIG_TRACE_SYSTEMTAP ifdef CONFIG_TRACE_SYSTEMTAP
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp
@@ -103,11 +102,9 @@ all: $(PROGS) stap
# Dummy command so that make thinks it has done something # Dummy command so that make thinks it has done something
@true @true
obj-y += trace/
######################################################### #########################################################
# cpu emulator library # cpu emulator library
obj-y += exec.o exec-vary.o obj-y += exec.o
obj-y += accel/ 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.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 obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
@@ -117,9 +114,6 @@ obj-$(CONFIG_TCG) += fpu/softfloat.o
obj-y += target/$(TARGET_BASE_ARCH)/ obj-y += target/$(TARGET_BASE_ARCH)/
obj-y += disas.o obj-y += disas.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
LIBS := $(libs_cpu) $(LIBS)
obj-$(CONFIG_PLUGIN) += plugins/
######################################################### #########################################################
# Linux user emulator target # Linux user emulator target
@@ -128,8 +122,7 @@ ifdef CONFIG_LINUX_USER
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \ QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
-I$(SRC_PATH)/linux-user/host/$(ARCH) \ -I$(SRC_PATH)/linux-user/host/$(ARCH) \
-I$(SRC_PATH)/linux-user \ -I$(SRC_PATH)/linux-user
-Ilinux-user/$(TARGET_ABI_DIR)
obj-y += linux-user/ obj-y += linux-user/
obj-y += gdbstub.o thunk.o obj-y += gdbstub.o thunk.o
@@ -152,12 +145,14 @@ endif #CONFIG_BSD_USER
######################################################### #########################################################
# System emulator target # System emulator target
ifdef CONFIG_SOFTMMU ifdef CONFIG_SOFTMMU
obj-y += softmmu/ obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
obj-y += gdbstub.o obj-y += qtest.o
obj-y += dump/
obj-y += hw/ obj-y += hw/
obj-y += monitor/
obj-y += qapi/ obj-y += qapi/
obj-y += memory.o
obj-y += memory_mapping.o
obj-y += dump.o
obj-$(TARGET_X86_64) += win_dump.o
obj-y += migration/ram.o obj-y += migration/ram.o
LIBS := $(libs_softmmu) $(LIBS) LIBS := $(libs_softmmu) $(LIBS)
@@ -168,54 +163,50 @@ else
obj-y += hw/$(TARGET_BASE_ARCH)/ obj-y += hw/$(TARGET_BASE_ARCH)/
endif endif
generated-files-y += hmp-commands.h hmp-commands-info.h GENERATED_FILES += hmp-commands.h hmp-commands-info.h
generated-files-y += config-devices.h
endif # CONFIG_SOFTMMU endif # CONFIG_SOFTMMU
dummy := $(call unnest-vars,,obj-y) dummy := $(call unnest-vars,,obj-y)
all-obj-y := $(obj-y) all-obj-y := $(obj-y)
# target-obj-y :=
# common-obj-m has some crap here, probably as side effect from block-obj-y :=
# unnest-vars recursing into target directories to fill obj-y and not common-obj-y :=
# properly handling the -m case. chardev-obj-y :=
# slirp-obj-y :=
# 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 include $(SRC_PATH)/Makefile.objs
dummy := $(call unnest-vars,,target-obj-y)
target-obj-y-save := $(target-obj-y)
dummy := $(call unnest-vars,.., \ dummy := $(call unnest-vars,.., \
authz-obj-y \ authz-obj-y \
block-obj-y \ block-obj-y \
block-obj-m \ block-obj-m \
chardev-obj-y \ chardev-obj-y \
crypto-obj-y \ crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \ qom-obj-y \
io-obj-y \ io-obj-y \
common-obj-y \ common-obj-y \
common-obj-m) common-obj-m \
slirp-obj-y)
target-obj-y := $(target-obj-y-save)
all-obj-y += $(common-obj-y) all-obj-y += $(common-obj-y)
all-obj-y += $(target-obj-y)
all-obj-y += $(qom-obj-y) all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(slirp-obj-y)
ifdef CONFIG_SOFTMMU
$(QEMU_PROG_BUILD): config-devices.mak $(QEMU_PROG_BUILD): config-devices.mak
endif
COMMON_LDADDS = ../libqemuutil.a COMMON_LDADDS = ../libqemuutil.a
# build either PROG or PROGW # 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, $^)) $(call LINK, $(filter-out %.mak, $^))
ifdef CONFIG_DARWIN ifdef CONFIG_DARWIN
$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
@@ -235,27 +226,10 @@ clean: clean-target
rm -f *.a *~ $(PROGS) rm -f *.a *~ $(PROGS)
rm -f $(shell find . -name '*.[od]') rm -f $(shell find . -name '*.[od]')
rm -f hmp-commands.h gdbstub-xml.c rm -f hmp-commands.h gdbstub-xml.c
rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
ifdef CONFIG_TRACE_SYSTEMTAP ifdef CONFIG_TRACE_SYSTEMTAP
rm -f *.stp rm -f *.stp
endif 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 install: all
ifneq ($(PROGS),) ifneq ($(PROGS),)
$(call install-prog,$(PROGS),$(DESTDIR)$(bindir)) $(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
@@ -267,21 +241,5 @@ ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" $(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
endif endif
generated-files-y += config-target.h GENERATED_FILES += config-target.h
Makefile: $(generated-files-y) Makefile: $(GENERATED_FILES)
# Reports/Analysis
#
# The target specific coverage report only cares about target specific
# blobs and not the shared code.
#
%/coverage-report.html:
@mkdir -p $*
$(call quiet-command,\
gcovr -r $(SRC_PATH) --object-directory $(CURDIR) \
-p --html --html-details -o $@, \
"GEN", "coverage-report.html")
.PHONY: coverage-report
coverage-report: $(CURDIR)/reports/coverage/coverage-report.html

View File

@@ -1,6 +1,5 @@
=========== QEMU README
QEMU README ===========
===========
QEMU is a generic and open source machine & userspace emulator and QEMU is a generic and open source machine & userspace emulator and
virtualizer. 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 Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
of other UNIX targets. The simple steps to build QEMU are: of other UNIX targets. The simple steps to build QEMU are:
.. code-block:: shell
mkdir build mkdir build
cd build cd build
../configure ../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: Additional information can also be found online via the QEMU website:
* `<https://qemu.org/Hosts/Linux>`_ https://qemu.org/Hosts/Linux
* `<https://qemu.org/Hosts/Mac>`_ https://qemu.org/Hosts/Mac
* `<https://qemu.org/Hosts/W32>`_ https://qemu.org/Hosts/W32
Submitting patches Submitting patches
@@ -58,29 +54,24 @@ Submitting patches
The QEMU source code is maintained under the GIT version control system. The QEMU source code is maintained under the GIT version control system.
.. code-block:: shell
git clone https://git.qemu.org/git/qemu.git git clone https://git.qemu.org/git/qemu.git
When submitting patches, one common approach is to use '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 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 qemu-devel@nongnu.org mailing list. All patches submitted must contain
a 'Signed-off-by' line from the author. Patches should follow the 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 Additional information on submitting patches can be found online via
the QEMU website the QEMU website
* `<https://qemu.org/Contribute/SubmitAPatch>`_ https://qemu.org/Contribute/SubmitAPatch
* `<https://qemu.org/Contribute/TrivialPatches>`_ https://qemu.org/Contribute/TrivialPatches
The QEMU website is also maintained under source control. The QEMU website is also maintained under source control.
.. code-block:: shell
git clone https://git.qemu.org/git/qemu-web.git 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 A 'git-publish' utility was created to make above process less
cumbersome, and is highly recommended for making regular contributions, cumbersome, and is highly recommended for making regular contributions,
@@ -91,12 +82,10 @@ manually for once.
For installation instructions, please go to For installation instructions, please go to
* `<https://github.com/stefanha/git-publish>`_ https://github.com/stefanha/git-publish
The workflow with 'git-publish' is: The workflow with 'git-publish' is:
.. code-block:: shell
$ git checkout master -b my-feature $ git checkout master -b my-feature
$ # work on new commits, add your 'Signed-off-by' lines to each $ # work on new commits, add your 'Signed-off-by' lines to each
$ git publish $ git publish
@@ -106,8 +95,6 @@ back to it in the future.
Sending v2: Sending v2:
.. code-block:: shell
$ git checkout my-feature # same topic branch $ git checkout my-feature # same topic branch
$ # making changes to the commits (using 'git rebase', for example) $ # making changes to the commits (using 'git rebase', for example)
$ git publish $ 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 found when running code built from QEMU git or upstream released sources
should be reported via: 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 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 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: For additional information on bug reporting consult:
* `<https://qemu.org/Contribute/ReportABug>`_ https://qemu.org/Contribute/ReportABug
Contact Contact
@@ -140,11 +127,13 @@ Contact
The QEMU community can be contacted in a number of ways, with the two The QEMU community can be contacted in a number of ways, with the two
main methods being email and IRC main methods being email and IRC
* `<mailto:qemu-devel@nongnu.org>`_ - qemu-devel@nongnu.org
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_ https://lists.nongnu.org/mailman/listinfo/qemu-devel
* #qemu on irc.oftc.net - #qemu on irc.oftc.net
Information on additional methods of contacting the community can be Information on additional methods of contacting the community can be
found online via the QEMU website: found online via the QEMU website:
* `<https://qemu.org/Contribute/StartHere>`_ https://qemu.org/Contribute/StartHere
-- End

View File

@@ -1 +1 @@
5.0.92 3.1.50

View File

@@ -1,9 +0,0 @@
config TCG
bool
config KVM
bool
config XEN
bool
select FSDEV_9P if VIRTFS

View File

@@ -1,6 +1,4 @@
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_KVM) += kvm/
obj-$(CONFIG_TCG) += tcg/ obj-$(CONFIG_TCG) += tcg/
obj-$(CONFIG_XEN) += xen/
obj-y += stubs/ obj-y += stubs/

View File

@@ -28,7 +28,13 @@
#include "hw/boards.h" #include "hw/boards.h"
#include "sysemu/arch_init.h" #include "sysemu/arch_init.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
#include "hw/xen/xen.h"
#include "qom/object.h" #include "qom/object.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qapi/error.h"
static const TypeInfo accel_type = { static const TypeInfo accel_type = {
.name = TYPE_ACCEL, .name = TYPE_ACCEL,
@@ -38,7 +44,7 @@ static const TypeInfo accel_type = {
}; };
/* Lookup AccelClass from opt_name. Returns NULL if not found */ /* 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); char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
@@ -46,9 +52,11 @@ AccelClass *accel_find(const char *opt_name)
return ac; 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; int ret;
ms->accelerator = accel; ms->accelerator = accel;
*(acc->allowed) = true; *(acc->allowed) = true;
@@ -57,15 +65,70 @@ int accel_init_machine(AccelState *accel, MachineState *ms)
ms->accelerator = NULL; ms->accelerator = NULL;
*(acc->allowed) = false; *(acc->allowed) = false;
object_unref(OBJECT(accel)); object_unref(OBJECT(accel));
} else {
object_set_accelerator_compat_props(acc->compat_props);
} }
return ret; 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 "No default accelerator available"
#endif
}
}
accel_list = g_strsplit(accel, ":", 0);
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
acc = accel_find(*tmp);
if (!acc) {
continue;
}
if (acc->available && !acc->available()) {
printf("%s not supported for this target\n",
acc->name);
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) void accel_setup_post(MachineState *ms)

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# See docs/devel/tracing.txt for syntax documentation. # Trace events for debugging and performance instrumentation
# kvm-all.c # kvm-all.c
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
@@ -15,6 +15,4 @@ kvm_irqchip_release_virq(int virq) "virq %d"
kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d"
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_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_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"

View File

@@ -1,54 +0,0 @@
/*
* QTest accelerator code
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.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 "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "sysemu/accel.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
static int qtest_init_accel(MachineState *ms)
{
QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0,
&error_abort);
qemu_opt_set(opts, "shift", "0", &error_abort);
configure_icount(opts, &error_abort);
qemu_opts_del(opts);
return 0;
}
static void qtest_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "QTest";
ac->init_machine = qtest_init_accel;
ac->allowed = &qtest_allowed;
}
#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest")
static const TypeInfo qtest_accel_type = {
.name = TYPE_QTEST_ACCEL,
.parent = TYPE_ACCEL,
.class_init = qtest_accel_class_init,
};
static void qtest_type_init(void)
{
type_register_static(&qtest_accel_type);
}
type_init(qtest_type_init);

View File

@@ -3,4 +3,3 @@ obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
obj-$(call lnot,$(CONFIG_XEN)) += xen-stub.o

View File

@@ -14,6 +14,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/hax.h" #include "sysemu/hax.h"

View File

@@ -12,6 +12,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/hvf.h" #include "sysemu/hvf.h"

View File

@@ -11,6 +11,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
@@ -78,6 +79,10 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
return -ENOSYS; return -ENOSYS;
} }
void kvm_set_singlestep(CPUState *cs, int enabled)
{
}
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type) target_ulong len, int type)
{ {
@@ -138,18 +143,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) int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
{ {
return -ENOSYS; return -ENOSYS;

View File

@@ -11,8 +11,10 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "tcg/tcg.h" #include "tcg/tcg.h"
#include "exec/cpu-common.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
void tb_flush(CPUState *cpu) void tb_flush(CPUState *cpu)
@@ -22,10 +24,3 @@ void tb_flush(CPUState *cpu)
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) 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();
}

View File

@@ -9,6 +9,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/whpx.h" #include "sysemu/whpx.h"

View File

@@ -6,4 +6,3 @@ obj-y += translator.o
obj-$(CONFIG_USER_ONLY) += user-exec.o obj-$(CONFIG_USER_ONLY) += user-exec.o
obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o
obj-$(CONFIG_PLUGIN) += plugin-gen.o

View File

@@ -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);
}

View File

@@ -18,7 +18,6 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "qemu/plugin.h"
#include "trace/mem.h" #include "trace/mem.h"
#if DATA_SIZE == 16 #if DATA_SIZE == 16
@@ -60,14 +59,37 @@
# define ABI_TYPE uint32_t # define ABI_TYPE uint32_t
#endif #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_GET_CPU(env), addr, info); \
trace_guest_mem_before_exec(ENV_GET_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_GET_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_GET_CPU(env), addr, info); \
} while (0)
/* Define host-endian atomic operations. Note that END is used within /* Define host-endian atomic operations. Note that END is used within
the ATOMIC_NAME macro, and redefined below. */ the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1 #if DATA_SIZE == 1
# define END # define END
# define MEND _be /* either le or be would be fine */
#elif defined(HOST_WORDS_BIGENDIAN) #elif defined(HOST_WORDS_BIGENDIAN)
# define END _be # define END _be
# define MEND _be
#else #else
# define END _le # define END _le
# define MEND _le
#endif #endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, 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; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret; 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 #if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, cmpv, newv); ret = atomic16_cmpxchg(haddr, cmpv, newv);
#else #else
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
#endif #endif
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return ret; return ret;
} }
@@ -96,13 +115,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; 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); val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
return val; return val;
} }
@@ -111,13 +127,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 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); atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
} }
#endif #endif
#else #else
@@ -127,29 +140,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret; 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); ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return ret; return ret;
} }
#define GEN_ATOMIC_HELPER(X) \ #define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \ ABI_TYPE val EXTRA_ARGS) \
{ \ { \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \ DATA_TYPE ret; \
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \ \
ATOMIC_MMU_IDX); \ ATOMIC_TRACE_RMW; \
atomic_trace_rmw_pre(env, addr, info); \
ret = atomic_##X(haddr, val); \ ret = atomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return ret; \ return ret; \
} }
@@ -178,9 +186,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE cmp, old, new, val = xval; \ XDATA_TYPE cmp, old, new, val = xval; \
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \ \
ATOMIC_MMU_IDX); \ ATOMIC_TRACE_RMW; \
atomic_trace_rmw_pre(env, addr, info); \
smp_mb(); \ smp_mb(); \
cmp = atomic_read__nocheck(haddr); \ cmp = atomic_read__nocheck(haddr); \
do { \ do { \
@@ -188,7 +195,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \ cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \ } while (cmp != old); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return RET; \ return RET; \
} }
@@ -206,6 +212,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#endif /* DATA SIZE >= 16 */ #endif /* DATA SIZE >= 16 */
#undef END #undef END
#undef MEND
#if DATA_SIZE > 1 #if DATA_SIZE > 1
@@ -213,8 +220,10 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
within the ATOMIC_NAME macro. */ within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
# define END _le # define END _le
# define MEND _le
#else #else
# define END _be # define END _be
# define MEND _be
#endif #endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, 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; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret; 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 #if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
#else #else
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
#endif #endif
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return BSWAP(ret); return BSWAP(ret);
} }
@@ -243,13 +249,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; 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); val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
return BSWAP(val); return BSWAP(val);
} }
@@ -258,15 +261,11 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 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;
atomic_trace_st_pre(env, addr, info);
val = BSWAP(val); val = BSWAP(val);
atomic16_set(haddr, val); atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
} }
#endif #endif
#else #else
@@ -276,29 +275,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ABI_TYPE ret; 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)); ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return BSWAP(ret); return BSWAP(ret);
} }
#define GEN_ATOMIC_HELPER(X) \ #define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \ ABI_TYPE val EXTRA_ARGS) \
{ \ { \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \ DATA_TYPE ret; \
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \ \
false, ATOMIC_MMU_IDX); \ ATOMIC_TRACE_RMW; \
atomic_trace_rmw_pre(env, addr, info); \
ret = atomic_##X(haddr, BSWAP(val)); \ ret = atomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return BSWAP(ret); \ return BSWAP(ret); \
} }
@@ -325,9 +319,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE ldo, ldn, old, new, val = xval; \ 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; \
atomic_trace_rmw_pre(env, addr, info); \
smp_mb(); \ smp_mb(); \
ldn = atomic_read__nocheck(haddr); \ ldn = atomic_read__nocheck(haddr); \
do { \ do { \
@@ -335,7 +328,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \ } while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return RET; \ return RET; \
} }
@@ -360,8 +352,13 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#endif /* DATA_SIZE >= 16 */ #endif /* DATA_SIZE >= 16 */
#undef END #undef END
#undef MEND
#endif /* DATA_SIZE > 1 */ #endif /* DATA_SIZE > 1 */
#undef ATOMIC_TRACE_ST
#undef ATOMIC_TRACE_LD
#undef ATOMIC_TRACE_RMW
#undef BSWAP #undef BSWAP
#undef ABI_TYPE #undef ABI_TYPE
#undef DATA_TYPE #undef DATA_TYPE

View File

@@ -20,7 +20,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "sysemu/tcg.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
bool tcg_allowed; bool tcg_allowed;

View File

@@ -16,14 +16,12 @@
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "trace.h" #include "trace.h"
#include "disas/disas.h" #include "disas/disas.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "tcg/tcg.h" #include "tcg.h"
#include "qemu/atomic.h" #include "qemu/atomic.h"
#include "sysemu/qtest.h" #include "sysemu/qtest.h"
#include "qemu/timer.h" #include "qemu/timer.h"
@@ -56,7 +54,7 @@ typedef struct SyncClocks {
#define MAX_DELAY_PRINT_RATE 2000000000LL #define MAX_DELAY_PRINT_RATE 2000000000LL
#define MAX_NB_PRINTS 100 #define MAX_NB_PRINTS 100
static void align_clocks(SyncClocks *sc, CPUState *cpu) static void align_clocks(SyncClocks *sc, const CPUState *cpu)
{ {
int64_t cpu_icount; int64_t cpu_icount;
@@ -64,7 +62,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
return; return;
} }
cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low; cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount); sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
sc->last_cpu_icount = cpu_icount; sc->last_cpu_icount = cpu_icount;
@@ -107,15 +105,15 @@ static void print_delay(const SyncClocks *sc)
} }
} }
static void init_delay_params(SyncClocks *sc, CPUState *cpu) static void init_delay_params(SyncClocks *sc,
const CPUState *cpu)
{ {
if (!icount_align_option) { if (!icount_align_option) {
return; return;
} }
sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock;
sc->last_cpu_icount sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
= cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
if (sc->diff_clk < max_delay) { if (sc->diff_clk < max_delay) {
max_delay = sc->diff_clk; max_delay = sc->diff_clk;
} }
@@ -156,7 +154,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
#if defined(DEBUG_DISAS) #if defined(DEBUG_DISAS)
if (qemu_loglevel_mask(CPU_LOG_TB_CPU) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
&& qemu_log_in_addr_range(itb->pc)) { && qemu_log_in_addr_range(itb->pc)) {
FILE *logfile = qemu_log_lock(); qemu_log_lock();
int flags = 0; int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU; flags |= CPU_DUMP_FPU;
@@ -165,10 +163,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
flags |= CPU_DUMP_CCOP; flags |= CPU_DUMP_CCOP;
#endif #endif
log_cpu_state(cpu, flags); log_cpu_state(cpu, flags);
qemu_log_unlock(logfile); qemu_log_unlock();
} }
#endif /* DEBUG_DISAS */ #endif /* DEBUG_DISAS */
cpu->can_do_io = !use_icount;
ret = tcg_qemu_tb_exec(env, tb_ptr); ret = tcg_qemu_tb_exec(env, tb_ptr);
cpu->can_do_io = 1; cpu->can_do_io = 1;
last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK);
@@ -238,10 +237,10 @@ void cpu_exec_step_atomic(CPUState *cpu)
uint32_t flags; uint32_t flags;
uint32_t cflags = 1; uint32_t cflags = 1;
uint32_t cf_mask = cflags & CF_HASH_MASK; 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) { if (sigsetjmp(cpu->jmp_env, 0) == 0) {
start_exclusive();
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) { if (tb == NULL) {
mmap_lock(); mmap_lock();
@@ -249,8 +248,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
mmap_unlock(); mmap_unlock();
} }
start_exclusive();
/* Since we got here, we know that parallel_cpus must be true. */ /* Since we got here, we know that parallel_cpus must be true. */
parallel_cpus = false; parallel_cpus = false;
in_exclusive_region = true;
cc->cpu_exec_enter(cpu); cc->cpu_exec_enter(cpu);
/* execute the generated code */ /* execute the generated code */
trace_exec_tb(tb, pc); trace_exec_tb(tb, pc);
@@ -268,18 +270,16 @@ void cpu_exec_step_atomic(CPUState *cpu)
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
} }
assert_no_pages_locked(); assert_no_pages_locked();
qemu_plugin_disable_mem_helpers(cpu);
} }
if (in_exclusive_region) {
/* /* We might longjump out of either the codegen or the
* As we start the exclusive region before codegen we must still * execution, so must make sure we only end the exclusive
* be in the region if we longjump out of either the codegen or * region if we started it.
* the execution. */
*/ parallel_cpus = true;
g_assert(cpu_in_exclusive_context(cpu)); end_exclusive();
parallel_cpus = true; }
end_exclusive();
} }
struct tb_desc { struct tb_desc {
@@ -467,7 +467,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
if (cpu->exception_index < 0) { if (cpu->exception_index < 0) {
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
if (replay_has_exception() if (replay_has_exception()
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) { && cpu->icount_decr.u16.low + cpu->icount_extra == 0) {
/* try to cause an exception pending in the log */ /* try to cause an exception pending in the log */
cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true); cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true);
} }
@@ -504,17 +504,6 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
cc->do_interrupt(cpu); cc->do_interrupt(cpu);
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
cpu->exception_index = -1; 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()) { } else if (!replay_has_interrupt()) {
/* give a chance to iothread in replay mode */ /* give a chance to iothread in replay mode */
*ret = EXCP_INTERRUPT; *ret = EXCP_INTERRUPT;
@@ -536,7 +525,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
* Ensure zeroing happens before reading cpu->exit_request or * Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also smp_wmb in cpu_exit()) * cpu->interrupt_request (see also smp_wmb in cpu_exit())
*/ */
atomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0); atomic_mb_set(&cpu->icount_decr.u16.high, 0);
if (unlikely(atomic_read(&cpu->interrupt_request))) { if (unlikely(atomic_read(&cpu->interrupt_request))) {
int interrupt_request; int interrupt_request;
@@ -588,13 +577,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
else { else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
replay_interrupt(); replay_interrupt();
/* cpu->exception_index = -1;
* 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);
*last_tb = NULL; *last_tb = NULL;
} }
/* The target hook may have updated the 'cpu->interrupt_request'; /* The target hook may have updated the 'cpu->interrupt_request';
@@ -613,9 +596,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
} }
/* Finally, check if we need to exit to the main loop. */ /* Finally, check if we need to exit to the main loop. */
if (unlikely(atomic_read(&cpu->exit_request)) if (unlikely(atomic_read(&cpu->exit_request)
|| (use_icount || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
atomic_set(&cpu->exit_request, 0); atomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) { if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT; cpu->exception_index = EXCP_INTERRUPT;
@@ -642,7 +624,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
} }
*last_tb = NULL; *last_tb = NULL;
insns_left = atomic_read(&cpu_neg(cpu)->icount_decr.u32); insns_left = atomic_read(&cpu->icount_decr.u32);
if (insns_left < 0) { if (insns_left < 0) {
/* Something asked us to stop executing chained TBs; just /* Something asked us to stop executing chained TBs; just
* continue round the main loop. Whatever requested the exit * continue round the main loop. Whatever requested the exit
@@ -661,7 +643,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
cpu_update_icount(cpu); cpu_update_icount(cpu);
/* Refill decrementer and continue execution. */ /* Refill decrementer and continue execution. */
insns_left = MIN(0xffff, cpu->icount_budget); insns_left = MIN(0xffff, cpu->icount_budget);
cpu_neg(cpu)->icount_decr.u16.low = insns_left; cpu->icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left; cpu->icount_extra = cpu->icount_budget - insns_left;
if (!cpu->icount_extra) { if (!cpu->icount_extra) {
/* Execute any remaining instructions, then let the main loop /* Execute any remaining instructions, then let the main loop
@@ -720,8 +702,6 @@ int cpu_exec(CPUState *cpu)
if (qemu_mutex_iothread_locked()) { if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
} }
qemu_plugin_disable_mem_helpers(cpu);
assert_no_pages_locked(); assert_no_pages_locked();
} }

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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

View File

@@ -0,0 +1,454 @@
/*
* Software MMU support
*
* Generate helpers used by TCG for qemu_ld/st ops and code load
* functions.
*
* Included from target op helpers and exec.c.
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#if DATA_SIZE == 8
#define SUFFIX q
#define LSUFFIX q
#define SDATA_TYPE int64_t
#define DATA_TYPE uint64_t
#elif DATA_SIZE == 4
#define SUFFIX l
#define LSUFFIX l
#define SDATA_TYPE int32_t
#define DATA_TYPE uint32_t
#elif DATA_SIZE == 2
#define SUFFIX w
#define LSUFFIX uw
#define SDATA_TYPE int16_t
#define DATA_TYPE uint16_t
#elif DATA_SIZE == 1
#define SUFFIX b
#define LSUFFIX ub
#define SDATA_TYPE int8_t
#define DATA_TYPE uint8_t
#else
#error unsupported data size
#endif
/* For the benefit of TCG generated code, we want to avoid the complication
of ABI-specific return type promotion and always return a value extended
to the register size of the host. This is tcg_target_long, except in the
case of a 32-bit host and 64-bit data, and for that we always have
uint64_t. Don't bother with this widened value for SOFTMMU_CODE_ACCESS. */
#if defined(SOFTMMU_CODE_ACCESS) || DATA_SIZE == 8
# define WORD_TYPE DATA_TYPE
# define USUFFIX SUFFIX
#else
# define WORD_TYPE tcg_target_ulong
# define USUFFIX glue(u, SUFFIX)
# define SSUFFIX glue(s, SUFFIX)
#endif
#ifdef SOFTMMU_CODE_ACCESS
#define READ_ACCESS_TYPE MMU_INST_FETCH
#define ADDR_READ addr_code
#else
#define READ_ACCESS_TYPE MMU_DATA_LOAD
#define ADDR_READ addr_read
#endif
#if DATA_SIZE == 8
# define BSWAP(X) bswap64(X)
#elif DATA_SIZE == 4
# define BSWAP(X) bswap32(X)
#elif DATA_SIZE == 2
# define BSWAP(X) bswap16(X)
#else
# define BSWAP(X) (X)
#endif
#if DATA_SIZE == 1
# define helper_le_ld_name glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
# define helper_be_ld_name helper_le_ld_name
# define helper_le_lds_name glue(glue(helper_ret_ld, SSUFFIX), MMUSUFFIX)
# define helper_be_lds_name helper_le_lds_name
# define helper_le_st_name glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)
# define helper_be_st_name helper_le_st_name
#else
# define helper_le_ld_name glue(glue(helper_le_ld, USUFFIX), MMUSUFFIX)
# define helper_be_ld_name glue(glue(helper_be_ld, USUFFIX), MMUSUFFIX)
# define helper_le_lds_name glue(glue(helper_le_ld, SSUFFIX), MMUSUFFIX)
# define helper_be_lds_name glue(glue(helper_be_ld, SSUFFIX), MMUSUFFIX)
# define helper_le_st_name glue(glue(helper_le_st, SUFFIX), MMUSUFFIX)
# define helper_be_st_name glue(glue(helper_be_st, SUFFIX), MMUSUFFIX)
#endif
#ifndef SOFTMMU_CODE_ACCESS
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
size_t mmu_idx, size_t index,
target_ulong addr,
uintptr_t retaddr,
bool recheck,
MMUAccessType access_type)
{
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
access_type, DATA_SIZE);
}
#endif
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
{
uintptr_t mmu_idx = get_mmuidx(oi);
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
target_ulong tlb_addr = entry->ADDR_READ;
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
DATA_TYPE res;
if (addr & ((1 << a_bits) - 1)) {
cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE,
mmu_idx, retaddr);
}
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
mmu_idx, retaddr);
index = tlb_index(env, mmu_idx, addr);
entry = tlb_entry(env, mmu_idx, addr);
}
tlb_addr = entry->ADDR_READ;
}
/* Handle an IO access. */
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
if ((addr & (DATA_SIZE - 1)) != 0) {
goto do_unaligned_access;
}
/* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
tlb_addr & TLB_RECHECK,
READ_ACCESS_TYPE);
res = TGT_LE(res);
return res;
}
/* Handle slow unaligned access (it spans two pages or IO). */
if (DATA_SIZE > 1
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
target_ulong addr1, addr2;
DATA_TYPE res1, res2;
unsigned shift;
do_unaligned_access:
addr1 = addr & ~(DATA_SIZE - 1);
addr2 = addr1 + DATA_SIZE;
res1 = helper_le_ld_name(env, addr1, oi, retaddr);
res2 = helper_le_ld_name(env, addr2, oi, retaddr);
shift = (addr & (DATA_SIZE - 1)) * 8;
/* Little-endian combine. */
res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
return res;
}
haddr = addr + entry->addend;
#if DATA_SIZE == 1
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
#else
res = glue(glue(ld, LSUFFIX), _le_p)((uint8_t *)haddr);
#endif
return res;
}
#if DATA_SIZE > 1
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
{
uintptr_t mmu_idx = get_mmuidx(oi);
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
target_ulong tlb_addr = entry->ADDR_READ;
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
DATA_TYPE res;
if (addr & ((1 << a_bits) - 1)) {
cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE,
mmu_idx, retaddr);
}
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
mmu_idx, retaddr);
index = tlb_index(env, mmu_idx, addr);
entry = tlb_entry(env, mmu_idx, addr);
}
tlb_addr = entry->ADDR_READ;
}
/* Handle an IO access. */
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
if ((addr & (DATA_SIZE - 1)) != 0) {
goto do_unaligned_access;
}
/* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
tlb_addr & TLB_RECHECK,
READ_ACCESS_TYPE);
res = TGT_BE(res);
return res;
}
/* Handle slow unaligned access (it spans two pages or IO). */
if (DATA_SIZE > 1
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
target_ulong addr1, addr2;
DATA_TYPE res1, res2;
unsigned shift;
do_unaligned_access:
addr1 = addr & ~(DATA_SIZE - 1);
addr2 = addr1 + DATA_SIZE;
res1 = helper_be_ld_name(env, addr1, oi, retaddr);
res2 = helper_be_ld_name(env, addr2, oi, retaddr);
shift = (addr & (DATA_SIZE - 1)) * 8;
/* Big-endian combine. */
res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
return res;
}
haddr = addr + entry->addend;
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
return res;
}
#endif /* DATA_SIZE > 1 */
#ifndef SOFTMMU_CODE_ACCESS
/* Provide signed versions of the load routines as well. We can of course
avoid this for 64-bit data, or for 32-bit data on 32-bit host. */
#if DATA_SIZE * 8 < TCG_TARGET_REG_BITS
WORD_TYPE helper_le_lds_name(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
{
return (SDATA_TYPE)helper_le_ld_name(env, addr, oi, retaddr);
}
# if DATA_SIZE > 1
WORD_TYPE helper_be_lds_name(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
{
return (SDATA_TYPE)helper_be_ld_name(env, addr, oi, retaddr);
}
# endif
#endif
static inline void glue(io_write, SUFFIX)(CPUArchState *env,
size_t mmu_idx, size_t index,
DATA_TYPE val,
target_ulong addr,
uintptr_t retaddr,
bool recheck)
{
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
recheck, DATA_SIZE);
}
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
{
uintptr_t mmu_idx = get_mmuidx(oi);
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
target_ulong tlb_addr = tlb_addr_write(entry);
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
if (addr & ((1 << a_bits) - 1)) {
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
mmu_idx, retaddr);
}
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
index = tlb_index(env, mmu_idx, addr);
entry = tlb_entry(env, mmu_idx, addr);
}
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
}
/* Handle an IO access. */
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
if ((addr & (DATA_SIZE - 1)) != 0) {
goto do_unaligned_access;
}
/* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */
val = TGT_LE(val);
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
retaddr, tlb_addr & TLB_RECHECK);
return;
}
/* Handle slow unaligned access (it spans two pages or IO). */
if (DATA_SIZE > 1
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
int i;
target_ulong page2;
CPUTLBEntry *entry2;
do_unaligned_access:
/* Ensure the second page is in the TLB. Note that the first page
is already guaranteed to be filled, and that the second page
cannot evict the first. */
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
entry2 = tlb_entry(env, mmu_idx, page2);
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
&& !VICTIM_TLB_HIT(addr_write, page2)) {
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
}
/* XXX: not efficient, but simple. */
/* This loop must go in the forward direction to avoid issues
with self-modifying code in Windows 64-bit. */
for (i = 0; i < DATA_SIZE; ++i) {
/* Little-endian extract. */
uint8_t val8 = val >> (i * 8);
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
oi, retaddr);
}
return;
}
haddr = addr + entry->addend;
#if DATA_SIZE == 1
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
#else
glue(glue(st, SUFFIX), _le_p)((uint8_t *)haddr, val);
#endif
}
#if DATA_SIZE > 1
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
{
uintptr_t mmu_idx = get_mmuidx(oi);
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
target_ulong tlb_addr = tlb_addr_write(entry);
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
if (addr & ((1 << a_bits) - 1)) {
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
mmu_idx, retaddr);
}
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
index = tlb_index(env, mmu_idx, addr);
entry = tlb_entry(env, mmu_idx, addr);
}
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
}
/* Handle an IO access. */
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
if ((addr & (DATA_SIZE - 1)) != 0) {
goto do_unaligned_access;
}
/* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */
val = TGT_BE(val);
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
tlb_addr & TLB_RECHECK);
return;
}
/* Handle slow unaligned access (it spans two pages or IO). */
if (DATA_SIZE > 1
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
int i;
target_ulong page2;
CPUTLBEntry *entry2;
do_unaligned_access:
/* Ensure the second page is in the TLB. Note that the first page
is already guaranteed to be filled, and that the second page
cannot evict the first. */
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
entry2 = tlb_entry(env, mmu_idx, page2);
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
&& !VICTIM_TLB_HIT(addr_write, page2)) {
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
}
/* XXX: not efficient, but simple */
/* This loop must go in the forward direction to avoid issues
with self-modifying code. */
for (i = 0; i < DATA_SIZE; ++i) {
/* Big-endian extract. */
uint8_t val8 = val >> (((DATA_SIZE - 1) * 8) - (i * 8));
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
oi, retaddr);
}
return;
}
haddr = addr + entry->addend;
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
}
#endif /* DATA_SIZE > 1 */
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
#undef READ_ACCESS_TYPE
#undef DATA_TYPE
#undef SUFFIX
#undef LSUFFIX
#undef DATA_SIZE
#undef ADDR_READ
#undef WORD_TYPE
#undef SDATA_TYPE
#undef USUFFIX
#undef SSUFFIX
#undef BSWAP
#undef helper_le_ld_name
#undef helper_be_ld_name
#undef helper_le_lds_name
#undef helper_be_lds_name
#undef helper_le_st_name
#undef helper_be_st_name

View File

@@ -25,29 +25,16 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "sysemu/accel.h" #include "sysemu/accel.h"
#include "sysemu/tcg.h" #include "sysemu/sysemu.h"
#include "qom/object.h" #include "qom/object.h"
#include "cpu.h" #include "qemu-common.h"
#include "qom/cpu.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "qemu/main-loop.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 { unsigned long tcg_tb_size;
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)
#ifndef CONFIG_USER_ONLY
/* mask must never be zero, except for A20 change call */ /* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask) static void tcg_handle_interrupt(CPUState *cpu, int mask)
{ {
@@ -64,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
if (!qemu_cpu_is_self(cpu)) { if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu); qemu_cpu_kick(cpu);
} else { } else {
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); atomic_set(&cpu->icount_decr.u16.high, -1);
if (use_icount && if (use_icount &&
!cpu->can_do_io !cpu->can_do_io
&& (mask & ~old_mask) != 0) { && (mask & ~old_mask) != 0) {
@@ -72,150 +59,29 @@ 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 #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) static int tcg_init(MachineState *ms)
{ {
TCGState *s = TCG_STATE(current_accel()); tcg_exec_init(tcg_tb_size * 1024 * 1024);
tcg_exec_init(s->tb_size * 1024 * 1024);
cpu_interrupt_handler = tcg_handle_interrupt; cpu_interrupt_handler = tcg_handle_interrupt;
mttcg_enabled = s->mttcg_enabled;
return 0; 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) static void tcg_accel_class_init(ObjectClass *oc, void *data)
{ {
AccelClass *ac = ACCEL_CLASS(oc); AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg"; ac->name = "tcg";
ac->init_machine = tcg_init; ac->init_machine = tcg_init;
ac->allowed = &tcg_allowed; 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 = { static const TypeInfo tcg_accel_type = {
.name = TYPE_TCG_ACCEL, .name = TYPE_TCG_ACCEL,
.parent = TYPE_ACCEL, .parent = TYPE_ACCEL,
.instance_init = tcg_accel_instance_init,
.class_init = tcg_accel_class_init, .class_init = tcg_accel_class_init,
.instance_size = sizeof(TCGState),
}; };
static void register_accel_types(void) static void register_accel_types(void)

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,6 @@
#include "exec/tb-lookup.h" #include "exec/tb-lookup.h"
#include "disas/disas.h" #include "disas/disas.h"
#include "exec/log.h" #include "exec/log.h"
#include "tcg/tcg.h"
/* 32-bit helpers */ /* 32-bit helpers */
@@ -147,7 +146,7 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg)
void *HELPER(lookup_tb_ptr)(CPUArchState *env) void *HELPER(lookup_tb_ptr)(CPUArchState *env)
{ {
CPUState *cpu = env_cpu(env); CPUState *cpu = ENV_GET_CPU(env);
TranslationBlock *tb; TranslationBlock *tb;
target_ulong cs_base, pc; target_ulong cs_base, pc;
uint32_t flags; uint32_t flags;
@@ -166,5 +165,5 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env)
void HELPER(exit_atomic)(CPUArchState *env) void HELPER(exit_atomic)(CPUArchState *env)
{ {
cpu_loop_exit_atomic(env_cpu(env), GETPC()); cpu_loop_exit_atomic(ENV_GET_CPU(env), GETPC());
} }

View File

@@ -225,11 +225,6 @@ DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
@@ -259,36 +254,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_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_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)
DEF_HELPER_FLAGS_4(gvec_shl64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sar8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
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_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_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) DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
@@ -318,5 +283,3 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)

View File

@@ -1,10 +1,10 @@
# See docs/devel/tracing.txt for syntax documentation. # Trace events for debugging and performance instrumentation
# TCG related tracing # TCG related tracing (mostly disabled by default)
# cpu-exec.c # cpu-exec.c
exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR disable 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 disable 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_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x"
# translate-all.c # translate-all.c
translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"

View File

@@ -16,17 +16,15 @@
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu-common.h"
#include "qemu-common.h"
#define NO_CPU_IO_DEFS #define NO_CPU_IO_DEFS
#include "cpu.h" #include "cpu.h"
#include "trace.h" #include "trace.h"
#include "disas/disas.h" #include "disas/disas.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "tcg/tcg.h" #include "tcg.h"
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
#include "qemu.h" #include "qemu.h"
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -52,12 +50,10 @@
#include "translate-all.h" #include "translate-all.h"
#include "qemu/bitmap.h" #include "qemu/bitmap.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/qemu-print.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "exec/log.h" #include "exec/log.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "sysemu/tcg.h"
/* #define DEBUG_TB_INVALIDATE */ /* #define DEBUG_TB_INVALIDATE */
/* #define DEBUG_TB_FLUSH */ /* #define DEBUG_TB_FLUSH */
@@ -173,13 +169,8 @@ struct page_collection {
#define TB_FOR_EACH_JMP(head_tb, tb, n) \ #define TB_FOR_EACH_JMP(head_tb, tb, n) \
TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next) 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,
* 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. */
* 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.
*/
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS #if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS
# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS # define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
@@ -187,7 +178,7 @@ struct page_collection {
# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS # define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS
#endif #endif
#else #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 #endif
/* Size of the L2 (and L3, etc) page tables. */ /* Size of the L2 (and L3, etc) page tables. */
@@ -372,7 +363,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
assert(use_icount); assert(use_icount);
/* Reset the cycle counter to the start of the block /* Reset the cycle counter to the start of the block
and shift if to the number of actually executed instructions */ and shift if to the number of actually executed instructions */
cpu_neg(cpu)->icount_decr.u16.low += num_insns - i; cpu->icount_decr.u16.low += num_insns - i;
} }
restore_state_to_opc(env, tb, data); restore_state_to_opc(env, tb, data);
@@ -384,11 +375,6 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
return 0; 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) bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
{ {
TranslationBlock *tb; TranslationBlock *tb;
@@ -418,7 +404,6 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
/* one-shot translation, invalidate it immediately */ /* one-shot translation, invalidate it immediately */
tb_phys_invalidate(tb, -1); tb_phys_invalidate(tb, -1);
tcg_tb_remove(tb); tcg_tb_remove(tb);
tb_destroy(tb);
} }
r = true; r = true;
} }
@@ -547,15 +532,6 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
#endif #endif
existing = atomic_cmpxchg(lp, NULL, pd); existing = atomic_cmpxchg(lp, NULL, pd);
if (unlikely(existing)) { 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); g_free(pd);
pd = existing; pd = existing;
} }
@@ -912,61 +888,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, /* 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. */ 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 /* 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 indicated, this is constrained by the range of direct branches on the
host cpu, as used by the TCG implementation of goto_tb. */ host cpu, as used by the TCG implementation of goto_tb. */
#if defined(__x86_64__) #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__) #elif defined(__sparc__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) # define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__powerpc64__) #elif defined(__powerpc64__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) # define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__powerpc__) #elif defined(__powerpc__)
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB) # define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024)
#elif defined(__aarch64__) #elif defined(__aarch64__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) # define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__s390x__) #elif defined(__s390x__)
/* We have a +- 4GB range on the branches; leave some slop. */ /* 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__) #elif defined(__mips__)
/* We have a 256MB branch region, but leave room to make sure the /* We have a 256MB branch region, but leave room to make sure the
main executable is also within that region. */ 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 #else
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) # define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
#endif #endif
#if TCG_TARGET_REG_BITS == 32 #define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32u * 1024 * 1024)
#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 \ #define DEFAULT_CODE_GEN_BUFFER_SIZE \
(DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \ (DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \
@@ -976,12 +934,15 @@ static inline size_t size_code_gen_buffer(size_t tb_size)
{ {
/* Size the buffer. */ /* Size the buffer. */
if (tb_size == 0) { if (tb_size == 0) {
size_t phys_mem = qemu_get_host_physmem(); #ifdef USE_STATIC_CODE_GEN_BUFFER
if (phys_mem == 0) { tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE; #else
} else { /* ??? Needs adjustments. */
tb_size = MIN(DEFAULT_CODE_GEN_BUFFER_SIZE, phys_mem / 8); /* ??? 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) { if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) {
tb_size = MIN_CODE_GEN_BUFFER_SIZE; tb_size = MIN_CODE_GEN_BUFFER_SIZE;
@@ -1068,20 +1029,47 @@ static inline void *alloc_code_gen_buffer(void)
{ {
int prot = PROT_WRITE | PROT_READ | PROT_EXEC; int prot = PROT_WRITE | PROT_READ | PROT_EXEC;
int flags = MAP_PRIVATE | MAP_ANONYMOUS; int flags = MAP_PRIVATE | MAP_ANONYMOUS;
uintptr_t start = 0;
size_t size = tcg_ctx->code_gen_buffer_size; size_t size = tcg_ctx->code_gen_buffer_size;
void *buf; 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) { if (buf == MAP_FAILED) {
return NULL; return NULL;
} }
#ifdef __mips__ #ifdef __mips__
if (cross_256mb(buf, size)) { if (cross_256mb(buf, size)) {
/* /* Try again, with the original still mapped, to avoid re-acquiring
* Try again, with the original still mapped, to avoid re-acquiring that 256mb crossing. This time don't specify an address. */
* the same 256mb crossing.
*/
size_t size2; size_t size2;
void *buf2 = mmap(NULL, size, prot, flags, -1, 0); void *buf2 = mmap(NULL, size, prot, flags, -1, 0);
switch ((int)(buf2 != MAP_FAILED)) { switch ((int)(buf2 != MAP_FAILED)) {
@@ -1165,6 +1153,23 @@ void tcg_exec_init(unsigned long tb_size)
#endif #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 */ /* call with @p->lock held */
static inline void invalidate_page_bitmap(PageDesc *p) static inline void invalidate_page_bitmap(PageDesc *p)
{ {
@@ -1223,8 +1228,6 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
/* flush all the translation blocks */ /* flush all the translation blocks */
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{ {
bool did_flush = false;
mmap_lock(); mmap_lock();
/* If it is already been done on request of another CPU, /* If it is already been done on request of another CPU,
* just retry. * just retry.
@@ -1232,7 +1235,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) { if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
goto done; goto done;
} }
did_flush = true;
if (DEBUG_TB_FLUSH_GATE) { if (DEBUG_TB_FLUSH_GATE) {
size_t nb_tbs = tcg_nb_tbs(); size_t nb_tbs = tcg_nb_tbs();
@@ -1257,22 +1259,14 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
done: done:
mmap_unlock(); mmap_unlock();
if (did_flush) {
qemu_plugin_flush_cb();
}
} }
void tb_flush(CPUState *cpu) void tb_flush(CPUState *cpu)
{ {
if (tcg_enabled()) { if (tcg_enabled()) {
unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count); unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
async_safe_run_on_cpu(cpu, do_tb_flush,
if (cpu_in_exclusive_context(cpu)) { RUN_ON_CPU_HOST_INT(tb_flush_count));
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));
}
} }
} }
@@ -1679,12 +1673,11 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tb_page_addr_t phys_pc, phys_page2; tb_page_addr_t phys_pc, phys_page2;
target_ulong virt_page2; target_ulong virt_page2;
tcg_insn_unit *gen_code_buf; tcg_insn_unit *gen_code_buf;
int gen_code_size, search_size, max_insns; int gen_code_size, search_size;
#ifdef CONFIG_PROFILER #ifdef CONFIG_PROFILER
TCGProfile *prof = &tcg_ctx->prof; TCGProfile *prof = &tcg_ctx->prof;
int64_t ti; int64_t ti;
#endif #endif
assert_memory_lock(); assert_memory_lock();
phys_pc = get_page_addr_code(env, pc); phys_pc = get_page_addr_code(env, pc);
@@ -1698,19 +1691,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
cflags &= ~CF_CLUSTER_MASK; cflags &= ~CF_CLUSTER_MASK;
cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT; cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT;
max_insns = cflags & CF_COUNT_MASK;
if (max_insns == 0) {
max_insns = CF_COUNT_MASK;
}
if (max_insns > TCG_MAX_INSNS) {
max_insns = TCG_MAX_INSNS;
}
if (cpu->singlestep_enabled || singlestep) {
max_insns = 1;
}
buffer_overflow: buffer_overflow:
tb = tcg_tb_alloc(tcg_ctx); tb = tb_alloc(pc);
if (unlikely(!tb)) { if (unlikely(!tb)) {
/* flush must be done */ /* flush must be done */
tb_flush(cpu); tb_flush(cpu);
@@ -1726,10 +1708,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tb->cs_base = cs_base; tb->cs_base = cs_base;
tb->flags = flags; tb->flags = flags;
tb->cflags = cflags; tb->cflags = cflags;
tb->orig_tb = NULL;
tb->trace_vcpu_dstate = *cpu->trace_dstate; tb->trace_vcpu_dstate = *cpu->trace_dstate;
tcg_ctx->tb_cflags = cflags; tcg_ctx->tb_cflags = cflags;
tb_overflow:
#ifdef CONFIG_PROFILER #ifdef CONFIG_PROFILER
/* includes aborted translations because of exceptions */ /* includes aborted translations because of exceptions */
@@ -1739,8 +1719,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tcg_func_start(tcg_ctx); tcg_func_start(tcg_ctx);
tcg_ctx->cpu = env_cpu(env); tcg_ctx->cpu = ENV_GET_CPU(env);
gen_intermediate_code(cpu, tb, max_insns); gen_intermediate_code(cpu, tb);
tcg_ctx->cpu = NULL; tcg_ctx->cpu = NULL;
trace_translate_block(tb, tb->pc, tb->tc.ptr); trace_translate_block(tb, tb->pc, tb->tc.ptr);
@@ -1763,39 +1743,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
ti = profile_getclock(); ti = profile_getclock();
#endif #endif
/* ??? Overflow could be handled better here. In particular, we
don't need to re-do gen_intermediate_code, nor should we re-do
the tcg optimization currently hidden inside tcg_gen_code. All
that should be required is to flush the TBs, allocate a new TB,
re-initialize it per above, and re-do the actual code generation. */
gen_code_size = tcg_gen_code(tcg_ctx, tb); gen_code_size = tcg_gen_code(tcg_ctx, tb);
if (unlikely(gen_code_size < 0)) { if (unlikely(gen_code_size < 0)) {
switch (gen_code_size) { goto buffer_overflow;
case -1:
/*
* Overflow of code_gen_buffer, or the current slice of it.
*
* TODO: We don't need to re-do gen_intermediate_code, nor
* should we re-do the tcg optimization currently hidden
* inside tcg_gen_code. All that should be required is to
* flush the TBs, allocate a new TB, re-initialize it per
* above, and re-do the actual code generation.
*/
goto buffer_overflow;
case -2:
/*
* The code generated for the TranslationBlock is too large.
* The maximum size allowed by the unwind info is 64k.
* There may be stricter constraints from relocations
* in the tcg backend.
*
* Try again with half as many insns as we attempted this time.
* If a single insn overflows, there's a bug somewhere...
*/
max_insns = tb->icount;
assert(max_insns > 1);
max_insns /= 2;
goto tb_overflow;
default:
g_assert_not_reached();
}
} }
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
if (unlikely(search_size < 0)) { if (unlikely(search_size < 0)) {
@@ -1813,44 +1768,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
#ifdef DEBUG_DISAS #ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
qemu_log_in_addr_range(tb->pc)) { qemu_log_in_addr_range(tb->pc)) {
FILE *logfile = qemu_log_lock(); 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("OUT: [size=%d]\n", gen_code_size); qemu_log("OUT: [size=%d]\n", gen_code_size);
if (tcg_ctx->data_gen_ptr) { if (tcg_ctx->data_gen_ptr) {
code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr; size_t code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr;
data_size = gen_code_size - code_size; size_t data_size = gen_code_size - code_size;
} else { size_t i;
code_size = gen_code_size;
}
/* Dump header and the first instruction */ log_disas(tb->tc.ptr, code_size);
chunk_start = tcg_ctx->gen_insn_end_off[insn];
log_disas(tb->tc.ptr, chunk_start, note->str);
/*
* 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)) { for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) {
if (sizeof(tcg_target_ulong) == 8) { if (sizeof(tcg_target_ulong) == 8) {
qemu_log("0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n", qemu_log("0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n",
@@ -1862,10 +1788,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
*(uint32_t *)(tcg_ctx->data_gen_ptr + i)); *(uint32_t *)(tcg_ctx->data_gen_ptr + i));
} }
} }
} else {
log_disas(tb->tc.ptr, gen_code_size);
} }
qemu_log("\n"); qemu_log("\n");
qemu_log_flush(); qemu_log_flush();
qemu_log_unlock(logfile); qemu_log_unlock();
} }
#endif #endif
@@ -1906,7 +1834,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize); orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize);
atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned);
tb_destroy(tb);
return existing_tb; return existing_tb;
} }
tcg_tb_insert(tb); tcg_tb_insert(tb);
@@ -1922,7 +1849,7 @@ static void
tb_invalidate_phys_page_range__locked(struct page_collection *pages, tb_invalidate_phys_page_range__locked(struct page_collection *pages,
PageDesc *p, tb_page_addr_t start, PageDesc *p, tb_page_addr_t start,
tb_page_addr_t end, tb_page_addr_t end,
uintptr_t retaddr) int is_cpu_write_access)
{ {
TranslationBlock *tb; TranslationBlock *tb;
tb_page_addr_t tb_start, tb_end; tb_page_addr_t tb_start, tb_end;
@@ -1930,9 +1857,9 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
#ifdef TARGET_HAS_PRECISE_SMC #ifdef TARGET_HAS_PRECISE_SMC
CPUState *cpu = current_cpu; CPUState *cpu = current_cpu;
CPUArchState *env = NULL; CPUArchState *env = NULL;
bool current_tb_not_found = retaddr != 0; int current_tb_not_found = is_cpu_write_access;
bool current_tb_modified = false;
TranslationBlock *current_tb = NULL; TranslationBlock *current_tb = NULL;
int current_tb_modified = 0;
target_ulong current_pc = 0; target_ulong current_pc = 0;
target_ulong current_cs_base = 0; target_ulong current_cs_base = 0;
uint32_t current_flags = 0; uint32_t current_flags = 0;
@@ -1964,21 +1891,24 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
if (!(tb_end <= start || tb_start >= end)) { if (!(tb_end <= start || tb_start >= end)) {
#ifdef TARGET_HAS_PRECISE_SMC #ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_not_found) { if (current_tb_not_found) {
current_tb_not_found = false; current_tb_not_found = 0;
/* now we have a real cpu fault */ current_tb = NULL;
current_tb = tcg_tb_lookup(retaddr); 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 && if (current_tb == tb &&
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
/* /* If we are modifying the current TB, we must stop
* If we are modifying the current TB, we must stop its execution. We could be more precise by checking
* its execution. We could be more precise by checking that the modification is after the current PC, but it
* that the modification is after the current PC, but it would require a specialized function to partially
* would require a specialized function to partially restore the CPU state */
* restore the CPU state.
*/ current_tb_modified = 1;
current_tb_modified = true; cpu_restore_state_from_tb(cpu, current_tb,
cpu_restore_state_from_tb(cpu, current_tb, retaddr, true); cpu->mem_io_pc, true);
cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base, cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
&current_flags); &current_flags);
} }
@@ -2013,7 +1943,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
* *
* Called with mmap_lock held for user-mode emulation * 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; struct page_collection *pages;
PageDesc *p; PageDesc *p;
@@ -2025,7 +1956,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
return; return;
} }
pages = page_collection_lock(start, end); 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); page_collection_unlock(pages);
} }
@@ -2072,8 +2004,7 @@ void tb_invalidate_phys_range(target_ulong start, target_ulong end)
* Call with all @pages in the range [@start, @start + len[ locked. * Call with all @pages in the range [@start, @start + len[ locked.
*/ */
void tb_invalidate_phys_page_fast(struct page_collection *pages, void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len, tb_page_addr_t start, int len)
uintptr_t retaddr)
{ {
PageDesc *p; PageDesc *p;
@@ -2100,8 +2031,7 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
} }
} else { } else {
do_invalidate: do_invalidate:
tb_invalidate_phys_page_range__locked(pages, p, start, start + len, tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1);
retaddr);
} }
} }
#else #else
@@ -2175,16 +2105,16 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc)
#endif #endif
/* user-mode: call with mmap_lock held */ /* user-mode: call with mmap_lock held */
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) void tb_check_watchpoint(CPUState *cpu)
{ {
TranslationBlock *tb; TranslationBlock *tb;
assert_memory_lock(); assert_memory_lock();
tb = tcg_tb_lookup(retaddr); tb = tcg_tb_lookup(cpu->mem_io_pc);
if (tb) { if (tb) {
/* We can use retranslation to find the PC. */ /* 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); tb_phys_invalidate(tb, -1);
} else { } else {
/* The exception probably happened in a helper. The CPU state should /* The exception probably happened in a helper. The CPU state should
@@ -2232,7 +2162,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
if ((env->hflags & MIPS_HFLAG_BMASK) != 0 if ((env->hflags & MIPS_HFLAG_BMASK) != 0
&& env->active_tc.PC != tb->pc) { && env->active_tc.PC != tb->pc) {
env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
cpu_neg(cpu)->icount_decr.u16.low++; cpu->icount_decr.u16.low++;
env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags &= ~MIPS_HFLAG_BMASK;
n = 2; n = 2;
} }
@@ -2240,7 +2170,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0 if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
&& env->pc != tb->pc) { && env->pc != tb->pc) {
env->pc -= 2; env->pc -= 2;
cpu_neg(cpu)->icount_decr.u16.low++; cpu->icount_decr.u16.low++;
env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
n = 2; n = 2;
} }
@@ -2256,7 +2186,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
tb_phys_invalidate(tb->orig_tb, -1); tb_phys_invalidate(tb->orig_tb, -1);
} }
tcg_tb_remove(tb); tcg_tb_remove(tb);
tb_destroy(tb);
} }
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
@@ -2285,7 +2214,8 @@ void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr)
tb_jmp_cache_clear_page(cpu, addr); tb_jmp_cache_clear_page(cpu, addr);
} }
static void print_qht_statistics(struct qht_stats hst) static void print_qht_statistics(FILE *f, fprintf_function cpu_fprintf,
struct qht_stats hst)
{ {
uint32_t hgram_opts; uint32_t hgram_opts;
size_t hgram_bins; size_t hgram_bins;
@@ -2294,7 +2224,7 @@ static void print_qht_statistics(struct qht_stats hst)
if (!hst.head_buckets) { if (!hst.head_buckets) {
return; return;
} }
qemu_printf("TB hash buckets %zu/%zu (%0.2f%% head buckets used)\n", cpu_fprintf(f, "TB hash buckets %zu/%zu (%0.2f%% head buckets used)\n",
hst.used_head_buckets, hst.head_buckets, hst.used_head_buckets, hst.head_buckets,
(double)hst.used_head_buckets / hst.head_buckets * 100); (double)hst.used_head_buckets / hst.head_buckets * 100);
@@ -2304,7 +2234,7 @@ static void print_qht_statistics(struct qht_stats hst)
hgram_opts |= QDIST_PR_NODECIMAL; hgram_opts |= QDIST_PR_NODECIMAL;
} }
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
qemu_printf("TB hash occupancy %0.2f%% avg chain occ. Histogram: %s\n", cpu_fprintf(f, "TB hash occupancy %0.2f%% avg chain occ. Histogram: %s\n",
qdist_avg(&hst.occupancy) * 100, hgram); qdist_avg(&hst.occupancy) * 100, hgram);
g_free(hgram); g_free(hgram);
@@ -2317,7 +2247,7 @@ static void print_qht_statistics(struct qht_stats hst)
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
} }
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
qemu_printf("TB hash avg chain %0.3f buckets. Histogram: %s\n", cpu_fprintf(f, "TB hash avg chain %0.3f buckets. Histogram: %s\n",
qdist_avg(&hst.chain), hgram); qdist_avg(&hst.chain), hgram);
g_free(hgram); g_free(hgram);
} }
@@ -2355,7 +2285,7 @@ static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
return false; return false;
} }
void dump_exec_info(void) void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
{ {
struct tb_tree_stats tst = {}; struct tb_tree_stats tst = {};
struct qht_stats hst; struct qht_stats hst;
@@ -2364,49 +2294,48 @@ void dump_exec_info(void)
tcg_tb_foreach(tb_tree_stats_iter, &tst); tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs; nb_tbs = tst.nb_tbs;
/* XXX: avoid using doubles ? */ /* XXX: avoid using doubles ? */
qemu_printf("Translation buffer state:\n"); cpu_fprintf(f, "Translation buffer state:\n");
/* /*
* Report total code size including the padding and TB structs; * Report total code size including the padding and TB structs;
* otherwise users might think "-tb-size" is not honoured. * otherwise users might think "-tb-size" is not honoured.
* For avg host size we use the precise numbers from tb_tree_stats though. * For avg host size we use the precise numbers from tb_tree_stats though.
*/ */
qemu_printf("gen code size %zu/%zu\n", cpu_fprintf(f, "gen code size %zu/%zu\n",
tcg_code_size(), tcg_code_capacity()); tcg_code_size(), tcg_code_capacity());
qemu_printf("TB count %zu\n", nb_tbs); cpu_fprintf(f, "TB count %zu\n", nb_tbs);
qemu_printf("TB avg target size %zu max=%zu bytes\n", cpu_fprintf(f, "TB avg target size %zu max=%zu bytes\n",
nb_tbs ? tst.target_size / nb_tbs : 0, nb_tbs ? tst.target_size / nb_tbs : 0,
tst.max_target_size); tst.max_target_size);
qemu_printf("TB avg host size %zu bytes (expansion ratio: %0.1f)\n", cpu_fprintf(f, "TB avg host size %zu bytes (expansion ratio: %0.1f)\n",
nb_tbs ? tst.host_size / nb_tbs : 0, nb_tbs ? tst.host_size / nb_tbs : 0,
tst.target_size ? (double)tst.host_size / tst.target_size : 0); tst.target_size ? (double)tst.host_size / tst.target_size : 0);
qemu_printf("cross page TB count %zu (%zu%%)\n", tst.cross_page, cpu_fprintf(f, "cross page TB count %zu (%zu%%)\n", tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
qemu_printf("direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n", cpu_fprintf(f, "direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n",
tst.direct_jmp_count, tst.direct_jmp_count,
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
tst.direct_jmp2_count, tst.direct_jmp2_count,
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst); qht_statistics_init(&tb_ctx.htable, &hst);
print_qht_statistics(hst); print_qht_statistics(f, cpu_fprintf, hst);
qht_statistics_destroy(&hst); qht_statistics_destroy(&hst);
qemu_printf("\nStatistics:\n"); cpu_fprintf(f, "\nStatistics:\n");
qemu_printf("TB flush count %u\n", cpu_fprintf(f, "TB flush count %u\n",
atomic_read(&tb_ctx.tb_flush_count)); atomic_read(&tb_ctx.tb_flush_count));
qemu_printf("TB invalidate count %zu\n", cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count());
tcg_tb_phys_invalidate_count());
tlb_flush_counts(&flush_full, &flush_part, &flush_elide); tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
qemu_printf("TLB full flushes %zu\n", flush_full); cpu_fprintf(f, "TLB full flushes %zu\n", flush_full);
qemu_printf("TLB partial flushes %zu\n", flush_part); cpu_fprintf(f, "TLB partial flushes %zu\n", flush_part);
qemu_printf("TLB elided flushes %zu\n", flush_elide); cpu_fprintf(f, "TLB elided flushes %zu\n", flush_elide);
tcg_dump_info(); tcg_dump_info(f, cpu_fprintf);
} }
void dump_opcount_info(void) void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
{ {
tcg_dump_op_count(); tcg_dump_op_count(f, cpu_fprintf);
} }
#else /* CONFIG_USER_ONLY */ #else /* CONFIG_USER_ONLY */
@@ -2415,7 +2344,7 @@ void cpu_interrupt(CPUState *cpu, int mask)
{ {
g_assert(qemu_mutex_iothread_locked()); g_assert(qemu_mutex_iothread_locked());
cpu->interrupt_request |= mask; cpu->interrupt_request |= mask;
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); atomic_set(&cpu->icount_decr.u16.high, -1);
} }
/* /*
@@ -2551,7 +2480,9 @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
/* This function should never be called with addresses outside the /* This function should never be called with addresses outside the
guest address space. If this assert fires, it probably indicates guest address space. If this assert fires, it probably indicates
a missing call to h2g_valid. */ 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(start < end);
assert_memory_lock(); assert_memory_lock();
@@ -2587,9 +2518,9 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
/* This function should never be called with addresses outside the /* This function should never be called with addresses outside the
guest address space. If this assert fires, it probably indicates guest address space. If this assert fires, it probably indicates
a missing call to h2g_valid. */ a missing call to h2g_valid. */
if (TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS) { #if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
} #endif
if (len == 0) { if (len == 0) {
return 0; return 0;

View File

@@ -27,10 +27,10 @@ struct page_collection *page_collection_lock(tb_page_addr_t start,
tb_page_addr_t end); tb_page_addr_t end);
void page_collection_unlock(struct page_collection *set); void page_collection_unlock(struct page_collection *set);
void tb_invalidate_phys_page_fast(struct page_collection *pages, void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len, 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_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, uintptr_t retaddr); void tb_check_watchpoint(CPUState *cpu);
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
int page_unprotect(target_ulong address, uintptr_t pc); int page_unprotect(target_ulong address, uintptr_t pc);

View File

@@ -8,6 +8,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "cpu.h" #include "cpu.h"
#include "tcg/tcg.h" #include "tcg/tcg.h"
@@ -16,7 +17,6 @@
#include "exec/gen-icount.h" #include "exec/gen-icount.h"
#include "exec/log.h" #include "exec/log.h"
#include "exec/translator.h" #include "exec/translator.h"
#include "exec/plugin-gen.h"
/* Pairs with tcg_clear_temp_count. /* Pairs with tcg_clear_temp_count.
To be called by #TranslatorOps.{translate_insn,tb_stop} if To be called by #TranslatorOps.{translate_insn,tb_stop} if
@@ -32,10 +32,9 @@ void translator_loop_temp_check(DisasContextBase *db)
} }
void translator_loop(const TranslatorOps *ops, DisasContextBase *db, void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
CPUState *cpu, TranslationBlock *tb, int max_insns) CPUState *cpu, TranslationBlock *tb)
{ {
int bp_insn = 0; int bp_insn = 0;
bool plugin_enabled;
/* Initialize DisasContext */ /* Initialize DisasContext */
db->tb = tb; db->tb = tb;
@@ -43,9 +42,20 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
db->pc_next = db->pc_first; db->pc_next = db->pc_first;
db->is_jmp = DISAS_NEXT; db->is_jmp = DISAS_NEXT;
db->num_insns = 0; db->num_insns = 0;
db->max_insns = max_insns;
db->singlestep_enabled = cpu->singlestep_enabled; db->singlestep_enabled = cpu->singlestep_enabled;
/* Instruction counting */
db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
if (db->max_insns == 0) {
db->max_insns = CF_COUNT_MASK;
}
if (db->max_insns > TCG_MAX_INSNS) {
db->max_insns = TCG_MAX_INSNS;
}
if (db->singlestep_enabled || singlestep) {
db->max_insns = 1;
}
ops->init_disas_context(db, cpu); ops->init_disas_context(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
@@ -57,17 +67,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->tb_start(db, cpu); ops->tb_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
plugin_enabled = plugin_gen_tb_start(cpu, tb);
while (true) { while (true) {
db->num_insns++; db->num_insns++;
ops->insn_start(db, cpu); ops->insn_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 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 */ /* Pass breakpoint hits to target for further processing */
if (!db->singlestep_enabled if (!db->singlestep_enabled
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
@@ -98,6 +102,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Accept I/O on the last instruction. */ /* Accept I/O on the last instruction. */
gen_io_start(); gen_io_start();
ops->translate_insn(db, cpu); ops->translate_insn(db, cpu);
gen_io_end();
} else { } else {
ops->translate_insn(db, cpu); ops->translate_insn(db, cpu);
} }
@@ -107,14 +112,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
break; 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, /* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */ or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
@@ -127,10 +124,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->tb_stop(db, cpu); ops->tb_stop(db, cpu);
gen_tb_end(db->tb, db->num_insns - bp_insn); 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. */ /* The disas_log hook may use these values rather than recompute. */
db->tb->size = db->pc_next - db->pc_first; db->tb->size = db->pc_next - db->pc_first;
db->tb->icount = db->num_insns; db->tb->icount = db->num_insns;
@@ -138,11 +131,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
#ifdef DEBUG_DISAS #ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
&& qemu_log_in_addr_range(db->pc_first)) { && qemu_log_in_addr_range(db->pc_first)) {
FILE *logfile = qemu_log_lock(); qemu_log_lock();
qemu_log("----------------\n"); qemu_log("----------------\n");
ops->disas_log(db, cpu); ops->disas_log(db, cpu);
qemu_log("\n"); qemu_log("\n");
qemu_log_unlock(logfile); qemu_log_unlock();
} }
#endif #endif
} }

View File

@@ -1,5 +1,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/core/cpu.h" #include "qemu-common.h"
#include "qom/cpu.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"

View File

@@ -20,14 +20,12 @@
#include "cpu.h" #include "cpu.h"
#include "disas/disas.h" #include "disas/disas.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "tcg/tcg.h" #include "tcg.h"
#include "qemu/bitops.h" #include "qemu/bitops.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "translate-all.h" #include "translate-all.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "qemu/atomic128.h" #include "qemu/atomic128.h"
#include "trace-root.h"
#include "trace/mem.h"
#undef EAX #undef EAX
#undef ECX #undef ECX
@@ -65,57 +63,28 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
{ {
CPUState *cpu = current_cpu; CPUState *cpu = current_cpu;
CPUClass *cc; CPUClass *cc;
int ret;
unsigned long address = (unsigned long)info->si_addr; unsigned long address = (unsigned long)info->si_addr;
MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
switch (helper_retaddr) { /* We must handle PC addresses from two different sources:
default: * a call return address and a signal frame address.
/* *
* Fault during host memory operation within a helper function. * Within cpu_restore_state_from_tb we assume the former and adjust
* The helper's host return address, saved here, gives us a * the address by -GETPC_ADJ so that the address is within the call
* pointer into the generated code that will unwind to the * insn so that addr does not accidentally match the beginning of the
* correct guest pc. * next guest insn.
*/ *
* However, when the PC comes from the signal frame, it points to
* the actual faulting host insn and not a call insn. Subtracting
* GETPC_ADJ in that case may accidentally match the previous guest insn.
*
* So for the later case, adjust forward to compensate for what
* will be done later by cpu_restore_state_from_tb.
*/
if (helper_retaddr) {
pc = helper_retaddr; pc = helper_retaddr;
break; } else {
case 0:
/*
* Fault during host memory operation within generated code.
* (Or, a unrelated bug within qemu, but we can't tell from here).
*
* We take the host pc from the signal frame. However, we cannot
* use that value directly. Within cpu_restore_state_from_tb, we
* assume PC comes from GETPC(), as used by the helper functions,
* so we adjust the address by -GETPC_ADJ to form an address that
* is within the call insn, so that the address does not accidentially
* match the beginning of the next guest insn. However, when the
* pc comes from the signal frame it points to the actual faulting
* host memory insn and not the return from a call insn.
*
* Therefore, adjust to compensate for what will be done later
* by cpu_restore_state_from_tb.
*/
pc += GETPC_ADJ; pc += GETPC_ADJ;
break;
case 1:
/*
* Fault during host read for translation, or loosely, "execution".
*
* The guest pc is already pointing to the start of the TB for which
* code is being generated. If the guest translator manages the
* page crossings correctly, this is exactly the correct address
* (and if the translator doesn't handle page boundaries correctly
* there's little we can do about that here). Therefore, do not
* trigger the unwinder.
*
* Like tb_gen_code, release the memory lock before cpu_loop_exit.
*/
pc = 0;
access_type = MMU_INST_FETCH;
mmap_unlock();
break;
} }
/* For synchronous signals we expect to be coming from the vCPU /* For synchronous signals we expect to be coming from the vCPU
@@ -165,7 +134,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
* currently executing TB was modified and must be exited * currently executing TB was modified and must be exited
* immediately. Clear helper_retaddr for next execution. * immediately. Clear helper_retaddr for next execution.
*/ */
clear_helper_retaddr(); helper_retaddr = 0;
cpu_exit_tb_from_sighandler(cpu, old_set); cpu_exit_tb_from_sighandler(cpu, old_set);
/* NORETURN */ /* NORETURN */
@@ -178,73 +147,35 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
are still valid segv ones */ are still valid segv ones */
address = h2g_nocheck(address); address = h2g_nocheck(address);
/*
* There is no way the target can handle this other than raising
* an exception. Undo signal and retaddr state prior to longjmp.
*/
sigprocmask(SIG_SETMASK, old_set, NULL);
clear_helper_retaddr();
cc = CPU_GET_CLASS(cpu); cc = CPU_GET_CLASS(cpu);
cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); /* see if it is an MMU fault */
g_assert_not_reached(); g_assert(cc->handle_mmu_fault);
} ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX);
static int probe_access_internal(CPUArchState *env, target_ulong addr, if (ret == 0) {
int fault_size, MMUAccessType access_type, /* The MMU fault was handled without causing real CPU fault.
bool nonfault, uintptr_t ra) * Retain helper_retaddr for a possible second fault.
{ */
int flags; return 1;
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) { /* All other paths lead to cpu_exit; clear helper_retaddr
if (nonfault) { * for next execution.
return TLB_INVALID_MASK; */
} else { helper_retaddr = 0;
CPUState *cpu = env_cpu(env);
CPUClass *cc = CPU_GET_CLASS(cpu); if (ret < 0) {
cc->tlb_fill(cpu, addr, fault_size, access_type, return 0; /* not an MMU fault */
MMU_USER_IDX, false, ra);
g_assert_not_reached();
}
} }
return 0;
}
int probe_access_flags(CPUArchState *env, target_ulong addr, /* Now we have a real cpu fault. */
MMUAccessType access_type, int mmu_idx, cpu_restore_state(cpu, pc, true);
bool nonfault, void **phost, uintptr_t ra)
{
int flags;
flags = probe_access_internal(env, addr, 0, access_type, nonfault, ra); sigprocmask(SIG_SETMASK, old_set, NULL);
*phost = flags ? NULL : g2h(addr); cpu_loop_exit(cpu);
return flags;
}
void *probe_access(CPUArchState *env, target_ulong addr, int size, /* never comes here */
MMUAccessType access_type, int mmu_idx, uintptr_t ra) return 1;
{
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(__i386__)
@@ -517,7 +448,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#if defined(__NetBSD__) #if defined(__NetBSD__)
#include <ucontext.h> #include <ucontext.h>
#include <sys/siginfo.h>
#endif #endif
int cpu_signal_handler(int host_signum, void *pinfo, int cpu_signal_handler(int host_signum, void *pinfo,
@@ -526,12 +456,10 @@ int cpu_signal_handler(int host_signum, void *pinfo,
siginfo_t *info = pinfo; siginfo_t *info = pinfo;
#if defined(__NetBSD__) #if defined(__NetBSD__)
ucontext_t *uc = puc; ucontext_t *uc = puc;
siginfo_t *si = pinfo;
#else #else
ucontext_t *uc = puc; ucontext_t *uc = puc;
#endif #endif
unsigned long pc; unsigned long pc;
uint32_t fsr;
int is_write; int is_write;
#if defined(__NetBSD__) #if defined(__NetBSD__)
@@ -542,48 +470,15 @@ int cpu_signal_handler(int host_signum, void *pinfo,
pc = uc->uc_mcontext.arm_pc; pc = uc->uc_mcontext.arm_pc;
#endif #endif
#ifdef __NetBSD__ /* error_code is the FSR value, in which bit 11 is WnR (assuming a v6 or
fsr = si->si_trap; * later processor; on v5 we will always report this as a read).
#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.
*/ */
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); return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
} }
#elif defined(__aarch64__) #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 #ifndef ESR_MAGIC
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
#define ESR_MAGIC 0x45535201 #define ESR_MAGIC 0x45535201
@@ -646,7 +541,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
} }
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
} }
#endif
#elif defined(__s390__) #elif defined(__s390__)
@@ -798,399 +692,26 @@ int cpu_signal_handler(int host_signum, void *pinfo,
/* The softmmu versions of these helpers are in cputlb.c. */ /* 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. */ /* Do not allow unaligned operations to proceed. Return the host address. */
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
int size, uintptr_t retaddr) int size, uintptr_t retaddr)
{ {
/* Enforce qemu required alignment. */ /* Enforce qemu required alignment. */
if (unlikely(addr & (size - 1))) { if (unlikely(addr & (size - 1))) {
cpu_loop_exit_atomic(env_cpu(env), retaddr); cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr);
} }
void *ret = g2h(addr); helper_retaddr = retaddr;
set_helper_retaddr(retaddr); return g2h(addr);
return ret;
} }
/* Macro to call the above, with local variables from the use context. */ /* Macro to call the above, with local variables from the use context. */
#define ATOMIC_MMU_DECLS do {} while (0) #define ATOMIC_MMU_DECLS do {} while (0)
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC()) #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_CLEANUP do { helper_retaddr = 0; } while (0)
#define ATOMIC_MMU_IDX MMU_USER_IDX
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) #define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
#define EXTRA_ARGS #define EXTRA_ARGS
#include "atomic_common.inc.c"
#define DATA_SIZE 1 #define DATA_SIZE 1
#include "atomic_template.h" #include "atomic_template.h"

View File

@@ -1 +0,0 @@
obj-y += xen-all.o

View File

@@ -22,11 +22,13 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/arch_init.h" #include "sysemu/arch_init.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/audio/soundhw.h" #include "hw/audio/soundhw.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
@@ -37,10 +39,6 @@
int graphic_width = 1024; int graphic_width = 1024;
int graphic_height = 768; int graphic_height = 768;
int graphic_depth = 8; int graphic_depth = 8;
#elif defined(TARGET_M68K)
int graphic_width = 800;
int graphic_height = 600;
int graphic_depth = 8;
#else #else
int graphic_width = 800; int graphic_width = 800;
int graphic_height = 600; int graphic_height = 600;
@@ -76,8 +74,6 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_PPC #define QEMU_ARCH QEMU_ARCH_PPC
#elif defined(TARGET_RISCV) #elif defined(TARGET_RISCV)
#define QEMU_ARCH QEMU_ARCH_RISCV #define QEMU_ARCH QEMU_ARCH_RISCV
#elif defined(TARGET_RX)
#define QEMU_ARCH QEMU_ARCH_RX
#elif defined(TARGET_S390X) #elif defined(TARGET_S390X)
#define QEMU_ARCH QEMU_ARCH_S390X #define QEMU_ARCH QEMU_ARCH_S390X
#elif defined(TARGET_SH4) #elif defined(TARGET_SH4)
@@ -90,8 +86,6 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_UNICORE32 #define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_XTENSA) #elif defined(TARGET_XTENSA)
#define QEMU_ARCH QEMU_ARCH_XTENSA #define QEMU_ARCH QEMU_ARCH_XTENSA
#elif defined(TARGET_AVR)
#define QEMU_ARCH QEMU_ARCH_AVR
#endif #endif
const uint32_t arch_type = QEMU_ARCH; const uint32_t arch_type = QEMU_ARCH;
@@ -113,3 +107,14 @@ int xen_available(void)
return 0; return 0;
#endif #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;
}

View File

@@ -1,7 +1,8 @@
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.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-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o common-obj-y += wavcapture.o
@@ -28,8 +29,3 @@ common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo
sdl.mo-objs = sdlaudio.o sdl.mo-objs = sdlaudio.o
sdl.mo-cflags := $(SDL_CFLAGS) sdl.mo-cflags := $(SDL_CFLAGS)
sdl.mo-libs := $(SDL_LIBS) sdl.mo-libs := $(SDL_LIBS)
# jack module
common-obj-$(CONFIG_AUDIO_JACK) += jack.mo
jack.mo-objs = jackaudio.o
jack.mo-libs := $(JACK_LIBS)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -26,32 +26,30 @@
#define QEMU_AUDIO_H #define QEMU_AUDIO_H
#include "qemu/queue.h" #include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
#include "hw/qdev-properties.h"
typedef void (*audio_callback_fn) (void *opaque, int avail); typedef void (*audio_callback_fn) (void *opaque, int avail);
typedef enum {
AUD_FMT_U8,
AUD_FMT_S8,
AUD_FMT_U16,
AUD_FMT_S16,
AUD_FMT_U32,
AUD_FMT_S32
} audfmt_e;
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
#define AUDIO_HOST_ENDIANNESS 1 #define AUDIO_HOST_ENDIANNESS 1
#else #else
#define AUDIO_HOST_ENDIANNESS 0 #define AUDIO_HOST_ENDIANNESS 0
#endif #endif
typedef struct audsettings { struct audsettings {
int freq; int freq;
int nchannels; int nchannels;
AudioFormat fmt; audfmt_e fmt;
int endianness; int endianness;
} audsettings; };
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
typedef enum { typedef enum {
AUD_CNOTIFY_ENABLE, AUD_CNOTIFY_ENABLE,
@@ -60,7 +58,7 @@ typedef enum {
struct audio_capture_ops { struct audio_capture_ops {
void (*notify) (void *opaque, audcnotification_e cmd); 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); void (*destroy) (void *opaque);
}; };
@@ -79,10 +77,8 @@ typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut; typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn; typedef struct SWVoiceIn SWVoiceIn;
typedef struct AudioState AudioState;
typedef struct QEMUSoundCard { typedef struct QEMUSoundCard {
char *name; char *name;
AudioState *state;
QLIST_ENTRY (QEMUSoundCard) entries; QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard; } QEMUSoundCard;
@@ -93,10 +89,10 @@ typedef struct QEMUAudioTimeStamp {
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_help (void);
void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture( CaptureVoiceOut *AUD_add_capture (
AudioState *s,
struct audsettings *as, struct audsettings *as,
struct audio_capture_ops *ops, struct audio_capture_ops *ops,
void *opaque void *opaque
@@ -113,7 +109,7 @@ SWVoiceOut *AUD_open_out (
); );
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); 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); int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on); void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw); 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_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); 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 ( SWVoiceIn *AUD_open_in (
QEMUSoundCard *card, QEMUSoundCard *card,
SWVoiceIn *sw, SWVoiceIn *sw,
@@ -144,7 +130,7 @@ SWVoiceIn *AUD_open_in (
); );
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); 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); void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw); int AUD_is_active_in (SWVoiceIn *sw);
@@ -157,25 +143,32 @@ static inline void *advance (void *p, int incr)
return (d + incr); return (d + incr);
} }
int wav_start_capture(AudioState *state, CaptureState *s, const char *path, #ifdef __GNUC__
int freq, int bits, int nchannels); #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); bool audio_is_cleaning_up(void);
void audio_cleanup(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); uint64_t *left, uint64_t *right);
void audio_sample_from_uint64(void *samples, int pos, void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right); uint64_t left, uint64_t right);
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 */ #endif /* QEMU_AUDIO_H */

View File

@@ -33,6 +33,22 @@
struct audio_pcm_ops; struct audio_pcm_ops;
typedef enum {
AUD_OPT_INT,
AUD_OPT_FMT,
AUD_OPT_STR,
AUD_OPT_BOOL
} audio_option_tag_e;
struct audio_option {
const char *name;
audio_option_tag_e tag;
void *valp;
const char *descr;
int *overriddenp;
int overridden;
};
struct audio_callback { struct audio_callback {
void *opaque; void *opaque;
audio_callback_fn fn; audio_callback_fn fn;
@@ -40,74 +56,66 @@ struct audio_callback {
struct audio_pcm_info { struct audio_pcm_info {
int bits; int bits;
bool is_signed; int sign;
bool is_float;
int freq; int freq;
int nchannels; int nchannels;
int bytes_per_frame; int align;
int shift;
int bytes_per_second; int bytes_per_second;
int swap_endianness; int swap_endianness;
}; };
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap; typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer {
size_t pos, size;
st_sample samples[];
} STSampleBuffer;
typedef struct HWVoiceOut { typedef struct HWVoiceOut {
AudioState *s;
int enabled; int enabled;
int poll_mode; int poll_mode;
int pending_disable; int pending_disable;
struct audio_pcm_info info; struct audio_pcm_info info;
f_sample *clip; f_sample *clip;
int rpos;
uint64_t ts_helper; uint64_t ts_helper;
STSampleBuffer *mix_buf; struct st_sample *mix_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
size_t samples; int samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops; struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceOut) entries; QLIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut; } HWVoiceOut;
typedef struct HWVoiceIn { typedef struct HWVoiceIn {
AudioState *s;
int enabled; int enabled;
int poll_mode; int poll_mode;
struct audio_pcm_info info; struct audio_pcm_info info;
t_sample *conv; t_sample *conv;
size_t total_samples_captured; int wpos;
int total_samples_captured;
uint64_t ts_helper; uint64_t ts_helper;
STSampleBuffer *conv_buf; struct st_sample *conv_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
size_t samples; int samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops; struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceIn) entries; QLIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn; } HWVoiceIn;
struct SWVoiceOut { struct SWVoiceOut {
QEMUSoundCard *card; QEMUSoundCard *card;
AudioState *s;
struct audio_pcm_info info; struct audio_pcm_info info;
t_sample *conv; t_sample *conv;
int64_t ratio; int64_t ratio;
struct st_sample *buf; struct st_sample *buf;
void *rate; void *rate;
size_t total_hw_samples_mixed; int total_hw_samples_mixed;
int active; int active;
int empty; int empty;
HWVoiceOut *hw; HWVoiceOut *hw;
@@ -119,12 +127,11 @@ struct SWVoiceOut {
struct SWVoiceIn { struct SWVoiceIn {
QEMUSoundCard *card; QEMUSoundCard *card;
AudioState *s;
int active; int active;
struct audio_pcm_info info; struct audio_pcm_info info;
int64_t ratio; int64_t ratio;
void *rate; void *rate;
size_t total_hw_samples_acquired; int total_hw_samples_acquired;
struct st_sample *buf; struct st_sample *buf;
f_sample *clip; f_sample *clip;
HWVoiceIn *hw; HWVoiceIn *hw;
@@ -138,7 +145,8 @@ typedef struct audio_driver audio_driver;
struct audio_driver { struct audio_driver {
const char *name; const char *name;
const char *descr; const char *descr;
void *(*init) (Audiodev *); struct audio_option *options;
void *(*init) (void);
void (*fini) (void *); void (*fini) (void *);
struct audio_pcm_ops *pcm_ops; struct audio_pcm_ops *pcm_ops;
int can_be_default; int can_be_default;
@@ -146,46 +154,24 @@ struct audio_driver {
int max_voices_in; int max_voices_in;
int voice_size_out; int voice_size_out;
int voice_size_in; int voice_size_in;
int ctl_caps;
QLIST_ENTRY(audio_driver) next; QLIST_ENTRY(audio_driver) next;
}; };
struct audio_pcm_ops { struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque); int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw); void (*fini_out)(HWVoiceOut *hw);
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size); int (*run_out) (HWVoiceOut *hw, int live);
void (*run_buffer_out)(HWVoiceOut *hw); int (*write) (SWVoiceOut *sw, void *buf, int size);
/* int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
* 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_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque); int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw); void (*fini_in) (HWVoiceIn *hw);
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size); int (*run_in) (HWVoiceIn *hw);
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size); int (*read) (SWVoiceIn *sw, void *buf, int size);
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size); int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
void (*enable_in)(HWVoiceIn *hw, bool enable);
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
}; };
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 capture_callback {
struct audio_capture_ops ops; struct audio_capture_ops ops;
void *opaque; void *opaque;
@@ -207,7 +193,6 @@ struct SWVoiceCap {
typedef struct AudioState { typedef struct AudioState {
struct audio_driver *drv; struct audio_driver *drv;
Audiodev *dev;
void *drv_opaque; void *drv_opaque;
QEMUTimer *ts; QEMUTimer *ts;
@@ -218,39 +203,36 @@ typedef struct AudioState {
int nb_hw_voices_out; int nb_hw_voices_out;
int nb_hw_voices_in; int nb_hw_voices_in;
int vm_running; int vm_running;
int64_t period_ticks;
bool timer_running;
uint64_t timer_last;
QTAILQ_ENTRY(AudioState) list;
} AudioState; } AudioState;
extern const struct mixeng_volume nominal_volume; extern const struct mixeng_volume nominal_volume;
extern const char *audio_prio_list[];
void audio_driver_register(audio_driver *drv); void audio_driver_register(audio_driver *drv);
audio_driver *audio_driver_lookup(const char *name); audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
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); int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size); 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 { #define VOICE_ENABLE 1
int64_t start_ticks; #define VOICE_DISABLE 2
int64_t bytes_sent; #define VOICE_VOLUME 3
} RateCtl;
void audio_rate_start(RateCtl *rate); #define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t bytes_avail);
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); return (dst >= src) ? (dst - src) : (len - src + dst);
} }
@@ -266,18 +248,4 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
#define AUDIO_STRINGIFY_(n) #n #define AUDIO_STRINGIFY_(n) #n
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
typedef struct AudiodevListEntry {
Audiodev *dev;
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
} AudiodevListEntry;
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
AudiodevListHead audio_handle_legacy_opts(void);
void audio_free_audiodev_list(AudiodevListHead *head);
void audio_create_pdos(Audiodev *dev);
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
#endif /* QEMU_AUDIO_INT_H */ #endif /* QEMU_AUDIO_INT_H */

View File

@@ -1,554 +0,0 @@
/*
* QEMU Audio subsystem: legacy configuration handling
*
* Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "audio.h"
#include "audio_int.h"
#include "qemu/cutils.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-audio.h"
#include "qapi/visitor-impl.h"
#define AUDIO_CAP "audio-legacy"
#include "audio_int.h"
static uint32_t toui32(const char *str)
{
unsigned long long ret;
if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
dolog("Invalid integer value `%s'\n", str);
exit(1);
}
return ret;
}
/* helper functions to convert env variables */
static void get_bool(const char *env, bool *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
*dst = toui32(val) != 0;
*has_dst = true;
}
}
static void get_int(const char *env, uint32_t *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
*dst = toui32(val);
*has_dst = true;
}
}
static void get_str(const char *env, char **dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
if (*has_dst) {
g_free(*dst);
}
*dst = g_strdup(val);
*has_dst = true;
}
}
static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
size_t i;
for (i = 0; AudioFormat_lookup.size; ++i) {
if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
*dst = i;
*has_dst = true;
return;
}
}
dolog("Invalid audio format `%s'\n", val);
exit(1);
}
}
static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
*dst = toui32(val) * 1000;
*has_dst = true;
}
}
static uint32_t frames_to_usecs(uint32_t frames,
AudiodevPerDirectionOptions *pdo)
{
uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
return (frames * 1000000 + freq / 2) / freq;
}
static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
AudiodevPerDirectionOptions *pdo)
{
const char *val = getenv(env);
if (val) {
*dst = frames_to_usecs(toui32(val), pdo);
*has_dst = true;
}
}
static uint32_t samples_to_usecs(uint32_t samples,
AudiodevPerDirectionOptions *pdo)
{
uint32_t channels = pdo->has_channels ? pdo->channels : 2;
return frames_to_usecs(samples / channels, pdo);
}
static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
AudiodevPerDirectionOptions *pdo)
{
const char *val = getenv(env);
if (val) {
*dst = samples_to_usecs(toui32(val), pdo);
*has_dst = true;
}
}
static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
{
AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt);
return samples_to_usecs(bytes / bytes_per_sample, pdo);
}
static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
AudiodevPerDirectionOptions *pdo)
{
const char *val = getenv(env);
if (val) {
*dst = bytes_to_usecs(toui32(val), pdo);
*has_dst = true;
}
}
/* backend specific functions */
/* ALSA */
static void handle_alsa_per_direction(
AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
{
char buf[64];
size_t len = strlen(prefix);
bool size_in_usecs = false;
bool dummy;
memcpy(buf, prefix, len);
strcpy(buf + len, "TRY_POLL");
get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
strcpy(buf + len, "DEV");
get_str(buf, &apdo->dev, &apdo->has_dev);
strcpy(buf + len, "SIZE_IN_USEC");
get_bool(buf, &size_in_usecs, &dummy);
strcpy(buf + len, "PERIOD_SIZE");
get_int(buf, &apdo->period_length, &apdo->has_period_length);
if (apdo->has_period_length && !size_in_usecs) {
apdo->period_length = frames_to_usecs(
apdo->period_length,
qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
}
strcpy(buf + len, "BUFFER_SIZE");
get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length);
if (apdo->has_buffer_length && !size_in_usecs) {
apdo->buffer_length = frames_to_usecs(
apdo->buffer_length,
qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
}
}
static void handle_alsa(Audiodev *dev)
{
AudiodevAlsaOptions *aopt = &dev->u.alsa;
handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_");
handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_");
get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
&aopt->threshold, &aopt->has_threshold);
}
/* coreaudio */
static void handle_coreaudio(Audiodev *dev)
{
get_frames_to_usecs(
"QEMU_COREAUDIO_BUFFER_SIZE",
&dev->u.coreaudio.out->buffer_length,
&dev->u.coreaudio.out->has_buffer_length,
qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out));
get_int("QEMU_COREAUDIO_BUFFER_COUNT",
&dev->u.coreaudio.out->buffer_count,
&dev->u.coreaudio.out->has_buffer_count);
}
/* dsound */
static void handle_dsound(Audiodev *dev)
{
get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS",
&dev->u.dsound.latency, &dev->u.dsound.has_latency);
get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT",
&dev->u.dsound.out->buffer_length,
&dev->u.dsound.out->has_buffer_length,
dev->u.dsound.out);
get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN",
&dev->u.dsound.in->buffer_length,
&dev->u.dsound.in->has_buffer_length,
dev->u.dsound.in);
}
/* OSS */
static void handle_oss_per_direction(
AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
const char *dev_env)
{
get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
get_str(dev_env, &opdo->dev, &opdo->has_dev);
get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
&opdo->buffer_length, &opdo->has_buffer_length,
qapi_AudiodevOssPerDirectionOptions_base(opdo));
get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count,
&opdo->has_buffer_count);
}
static void handle_oss(Audiodev *dev)
{
AudiodevOssOptions *oopt = &dev->u.oss;
handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL",
"QEMU_OSS_ADC_DEV");
handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL",
"QEMU_OSS_DAC_DEV");
get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap);
get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
}
/* pulseaudio */
static void handle_pa_per_direction(
AudiodevPaPerDirectionOptions *ppdo, const char *env)
{
get_str(env, &ppdo->name, &ppdo->has_name);
}
static void handle_pa(Audiodev *dev)
{
handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE");
handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK");
get_samples_to_usecs(
"QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length,
&dev->u.pa.in->has_buffer_length,
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in));
get_samples_to_usecs(
"QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length,
&dev->u.pa.out->has_buffer_length,
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server);
}
/* SDL */
static void handle_sdl(Audiodev *dev)
{
/* SDL is output only */
get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length,
&dev->u.sdl.out->has_buffer_length, dev->u.sdl.out);
}
/* wav */
static void handle_wav(Audiodev *dev)
{
get_int("QEMU_WAV_FREQUENCY",
&dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency);
get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format,
&dev->u.wav.out->has_format);
get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
&dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path);
}
/* general */
static void handle_per_direction(
AudiodevPerDirectionOptions *pdo, const char *prefix)
{
char buf[64];
size_t len = strlen(prefix);
memcpy(buf, prefix, len);
strcpy(buf + len, "FIXED_SETTINGS");
get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
strcpy(buf + len, "FIXED_FREQ");
get_int(buf, &pdo->frequency, &pdo->has_frequency);
strcpy(buf + len, "FIXED_FMT");
get_fmt(buf, &pdo->format, &pdo->has_format);
strcpy(buf + len, "FIXED_CHANNELS");
get_int(buf, &pdo->channels, &pdo->has_channels);
strcpy(buf + len, "VOICES");
get_int(buf, &pdo->voices, &pdo->has_voices);
}
static AudiodevListEntry *legacy_opt(const char *drvname)
{
AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
e->dev = g_malloc0(sizeof(Audiodev));
e->dev->id = g_strdup(drvname);
e->dev->driver = qapi_enum_parse(
&AudiodevDriver_lookup, drvname, -1, &error_abort);
audio_create_pdos(e->dev);
handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
/* Original description: Timer period in HZ (0 - use lowest possible) */
get_int("QEMU_AUDIO_TIMER_PERIOD",
&e->dev->timer_period, &e->dev->has_timer_period);
if (e->dev->has_timer_period && e->dev->timer_period) {
e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 /
e->dev->timer_period;
}
switch (e->dev->driver) {
case AUDIODEV_DRIVER_ALSA:
handle_alsa(e->dev);
break;
case AUDIODEV_DRIVER_COREAUDIO:
handle_coreaudio(e->dev);
break;
case AUDIODEV_DRIVER_DSOUND:
handle_dsound(e->dev);
break;
case AUDIODEV_DRIVER_OSS:
handle_oss(e->dev);
break;
case AUDIODEV_DRIVER_PA:
handle_pa(e->dev);
break;
case AUDIODEV_DRIVER_SDL:
handle_sdl(e->dev);
break;
case AUDIODEV_DRIVER_WAV:
handle_wav(e->dev);
break;
default:
break;
}
return e;
}
AudiodevListHead audio_handle_legacy_opts(void)
{
const char *drvname = getenv("QEMU_AUDIO_DRV");
AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
if (drvname) {
AudiodevListEntry *e;
audio_driver *driver = audio_driver_lookup(drvname);
if (!driver) {
dolog("Unknown audio driver `%s'\n", drvname);
exit(1);
}
e = legacy_opt(drvname);
QSIMPLEQ_INSERT_TAIL(&head, e, next);
} else {
for (int i = 0; audio_prio_list[i]; i++) {
audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
if (driver && driver->can_be_default) {
AudiodevListEntry *e = legacy_opt(driver->name);
QSIMPLEQ_INSERT_TAIL(&head, e, next);
}
}
if (QSIMPLEQ_EMPTY(&head)) {
dolog("Internal error: no default audio driver available\n");
exit(1);
}
}
return head;
}
/* visitor to print -audiodev option */
typedef struct {
Visitor visitor;
bool comma;
GList *path;
} LegacyPrintVisitor;
static bool 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)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
}
static void lv_print_key(Visitor *v, const char *name)
{
GList *e;
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
if (lv->comma) {
putchar(',');
} else {
lv->comma = true;
}
for (e = lv->path; e; e = e->next) {
if (e->data) {
printf("%s.", (const char *) e->data);
}
}
printf("%s=", name);
}
static bool 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,
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)
{
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)
{
const char *str = *obj;
lv_print_key(v, name);
while (*str) {
if (*str == ',') {
putchar(',');
}
putchar(*str++);
}
return true;
}
static void lv_complete(Visitor *v, void *opaque)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
assert(lv->path == NULL);
}
static void lv_free(Visitor *v)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
g_list_free_full(lv->path, g_free);
g_free(lv);
}
static Visitor *legacy_visitor_new(void)
{
LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
lv->visitor.start_struct = lv_start_struct;
lv->visitor.end_struct = lv_end_struct;
/* lists not supported */
lv->visitor.type_int64 = lv_type_int64;
lv->visitor.type_uint64 = lv_type_uint64;
lv->visitor.type_bool = lv_type_bool;
lv->visitor.type_str = lv_type_str;
lv->visitor.type = VISITOR_OUTPUT;
lv->visitor.complete = lv_complete;
lv->visitor.free = lv_free;
return &lv->visitor;
}
void audio_legacy_help(void)
{
AudiodevListHead head;
AudiodevListEntry *e;
printf("Environment variable based configuration deprecated.\n");
printf("Please use the new -audiodev option.\n");
head = audio_handle_legacy_opts();
printf("\nEquivalent -audiodev to your current environment variables:\n");
if (!getenv("QEMU_AUDIO_DRV")) {
printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
"possibilities)\n");
}
QSIMPLEQ_FOREACH(e, &head, next) {
Visitor *v;
Audiodev *dev = e->dev;
printf("-audiodev ");
v = legacy_visitor_new();
visit_type_Audiodev(v, NULL, &dev, &error_abort);
visit_free(v);
printf("\n");
}
audio_free_audiodev_list(&head);
}

174
audio/audio_pt_int.c Normal file
View File

@@ -0,0 +1,174 @@
#include "qemu/osdep.h"
#include "qemu-common.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;
}

22
audio/audio_pt_int.h Normal file
View File

@@ -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 */

View File

@@ -36,9 +36,9 @@
#define HWBUF hw->conv_buf #define HWBUF hw->conv_buf
#endif #endif
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
struct audio_driver *drv)
{ {
AudioState *s = &glob_audio_state;
int max_voices = glue (drv->max_voices_, TYPE); int max_voices = glue (drv->max_voices_, TYPE);
int voice_size = glue (drv->voice_size_, 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) static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{ {
g_free(hw->buf_emul);
g_free (HWBUF); g_free (HWBUF);
HWBUF = NULL; 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) { HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
size_t samples = hw->samples; if (!HWBUF) {
if (audio_bug(__func__, samples == 0)) { dolog ("Could not allocate " NAME " buffer (%d samples)\n",
dolog("Attempted to allocate empty buffer\n"); hw->samples);
} return -1;
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
HWBUF->size = samples;
} else {
HWBUF = NULL;
} }
return 0;
} }
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) 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; int samples;
if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) { samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
return 0;
}
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample)); sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) { 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; sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
#endif #endif
if (sw->info.is_float) {
#ifdef DAC #ifdef DAC
sw->conv = mixeng_conv_float[sw->info.nchannels == 2]; sw->conv = mixeng_conv
#else #else
sw->clip = mixeng_clip_float[sw->info.nchannels == 2]; sw->clip = mixeng_clip
#endif #endif
} else { [sw->info.nchannels == 2]
#ifdef DAC [sw->info.sign]
sw->conv = mixeng_conv [sw->info.swap_endianness]
#else [audio_bits_to_index (sw->info.bits)];
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->name = g_strdup (name); sw->name = g_strdup (name);
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); 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) static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{ {
AudioState *s = &glob_audio_state;
HW *hw = *hwp; HW *hw = *hwp;
AudioState *s = hw->s;
if (!hw->sw_head.lh_first) { if (!hw->sw_head.lh_first) {
#ifdef DAC #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; 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) { if (hw->enabled) {
return hw; return hw;
} }
@@ -230,10 +215,12 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
return NULL; return NULL;
} }
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw, static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
struct audsettings *as) 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)) { if (audio_pcm_info_eq (&hw->info, as)) {
return hw; return hw;
} }
@@ -241,10 +228,10 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
return NULL; return NULL;
} }
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
struct audsettings *as)
{ {
HW *hw; HW *hw;
AudioState *s = &glob_audio_state;
struct audio_driver *drv = s->drv; struct audio_driver *drv = s->drv;
if (!glue (s->nb_hw_voices_, TYPE)) { if (!glue (s->nb_hw_voices_, TYPE)) {
@@ -268,8 +255,8 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
return NULL; return NULL;
} }
hw->s = s;
hw->pcm_ops = drv->pcm_ops; hw->pcm_ops = drv->pcm_ops;
hw->ctl_caps = drv->ctl_caps;
QLIST_INIT (&hw->sw_head); QLIST_INIT (&hw->sw_head);
#ifdef DAC #ifdef DAC
@@ -280,29 +267,23 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
} }
if (audio_bug(__func__, hw->samples <= 0)) { if (audio_bug(__func__, hw->samples <= 0)) {
dolog("hw->samples=%zd\n", hw->samples); dolog ("hw->samples=%d\n", hw->samples);
goto err1; goto err1;
} }
if (hw->info.is_float) {
#ifdef DAC #ifdef DAC
hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; hw->clip = mixeng_clip
#else #else
hw->conv = mixeng_conv_float[hw->info.nchannels == 2]; hw->conv = mixeng_conv
#endif #endif
} else { [hw->info.nchannels == 2]
#ifdef DAC [hw->info.sign]
hw->clip = mixeng_clip [hw->info.swap_endianness]
#else [audio_bits_to_index (hw->info.bits)];
hw->conv = mixeng_conv
#endif
[hw->info.nchannels == 2]
[hw->info.is_signed]
[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); QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1; glue (s->nb_hw_voices_, TYPE) -= 1;
@@ -318,64 +299,31 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
return NULL; return NULL;
} }
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
{
switch (dev->driver) {
case AUDIODEV_DRIVER_NONE:
return dev->u.none.TYPE;
case AUDIODEV_DRIVER_ALSA:
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
case AUDIODEV_DRIVER_COREAUDIO:
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
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:
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
case AUDIODEV_DRIVER_SDL:
return dev->u.sdl.TYPE;
case AUDIODEV_DRIVER_SPICE:
return dev->u.spice.TYPE;
case AUDIODEV_DRIVER_WAV:
return dev->u.wav.TYPE;
case AUDIODEV_DRIVER__MAX:
break;
}
abort();
}
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
{ {
HW *hw; HW *hw;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (!pdo->mixing_engine || pdo->fixed_settings) { if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as); hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (!pdo->mixing_engine || hw) { if (hw) {
return 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) { if (hw) {
return hw; return hw;
} }
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as); hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) { if (hw) {
return 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)( static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
AudioState *s,
const char *sw_name, const char *sw_name,
struct audsettings *as struct audsettings *as
) )
@@ -383,10 +331,9 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
SW *sw; SW *sw;
HW *hw; HW *hw;
struct audsettings hw_as; struct audsettings hw_as;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) { if (glue (conf.fixed_, TYPE).enabled) {
hw_as = audiodev_to_audsettings(pdo); hw_as = glue (conf.fixed_, TYPE).settings;
} }
else { else {
hw_as = *as; hw_as = *as;
@@ -398,9 +345,8 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
sw_name ? sw_name : "unknown", sizeof (*sw)); sw_name ? sw_name : "unknown", sizeof (*sw));
goto err1; 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) { if (!hw) {
goto err2; goto err2;
} }
@@ -451,8 +397,7 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as struct audsettings *as
) )
{ {
AudioState *s; AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo;
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n", dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -460,9 +405,6 @@ SW *glue (AUD_open_, TYPE) (
goto fail; goto fail;
} }
s = card->state;
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n", ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
name, as->freq, as->nchannels, as->fmt); name, as->freq, as->nchannels, as->fmt);
@@ -480,7 +422,7 @@ SW *glue (AUD_open_, TYPE) (
return sw; return sw;
} }
if (!pdo->fixed_settings && sw) { if (!glue (conf.fixed_, TYPE).enabled && sw) {
glue (AUD_close_, TYPE) (card, sw); glue (AUD_close_, TYPE) (card, sw);
sw = NULL; sw = NULL;
} }
@@ -500,7 +442,7 @@ SW *glue (AUD_open_, TYPE) (
} }
} }
else { else {
sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as); sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
if (!sw) { if (!sw) {
dolog ("Failed to create voice `%s'\n", name); dolog ("Failed to create voice `%s'\n", name);
return NULL; return NULL;

View File

@@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
wfx->cbSize = 0; wfx->cbSize = 0;
switch (as->fmt) { switch (as->fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
wfx->wBitsPerSample = 8; wfx->wBitsPerSample = 8;
break; break;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
wfx->wBitsPerSample = 16; wfx->wBitsPerSample = 16;
wfx->nAvgBytesPerSec <<= 1; wfx->nAvgBytesPerSec <<= 1;
wfx->nBlockAlign <<= 1; wfx->nBlockAlign <<= 1;
break; break;
case AUDIO_FORMAT_S32: case AUD_FMT_S32:
case AUDIO_FORMAT_U32: case AUD_FMT_U32:
wfx->wBitsPerSample = 32; wfx->wBitsPerSample = 32;
wfx->nAvgBytesPerSec <<= 2; wfx->nAvgBytesPerSec <<= 2;
wfx->nBlockAlign <<= 2; wfx->nBlockAlign <<= 2;
@@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
switch (wfx->wBitsPerSample) { switch (wfx->wBitsPerSample) {
case 8: case 8:
as->fmt = AUDIO_FORMAT_U8; as->fmt = AUD_FMT_U8;
break; break;
case 16: case 16:
as->fmt = AUDIO_FORMAT_S16; as->fmt = AUD_FMT_S16;
break; break;
case 32: case 32:
as->fmt = AUDIO_FORMAT_S32; as->fmt = AUD_FMT_S32;
break; break;
default: default:

View File

@@ -26,7 +26,7 @@
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include <pthread.h> /* pthread_X */ #include <pthread.h> /* pthread_X */
#include "qemu/module.h" #include "qemu-common.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "coreaudio" #define AUDIO_CAP "coreaudio"
@@ -36,6 +36,11 @@
#define MAC_OS_X_VERSION_10_6 1060 #define MAC_OS_X_VERSION_10_6 1060
#endif #endif
typedef struct {
int buffer_frames;
int nbuffers;
} CoreaudioConf;
typedef struct coreaudioVoiceOut { typedef struct coreaudioVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
pthread_mutex_t mutex; pthread_mutex_t mutex;
@@ -43,6 +48,9 @@ typedef struct coreaudioVoiceOut {
UInt32 audioDevicePropertyBufferFrameSize; UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription; AudioStreamBasicDescription outputStreamBasicDescription;
AudioDeviceIOProcID ioprocid; AudioDeviceIOProcID ioprocid;
int live;
int decr;
int rpos;
} coreaudioVoiceOut; } coreaudioVoiceOut;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -394,29 +402,31 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
return 0; return 0;
} }
#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \ static int coreaudio_run_out (HWVoiceOut *hw, int live)
static ret_type glue(coreaudio_, name)args_decl \ {
{ \ int decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
ret_type ret; \
\ if (coreaudio_lock (core, "coreaudio_run_out")) {
if (coreaudio_lock(core, "coreaudio_" #name)) { \ return 0;
return 0; \
} \
\
ret = glue(audio_generic_, name)args; \
\
coreaudio_unlock(core, "coreaudio_" #name); \
return ret; \
} }
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size)) if (core->decr > live) {
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, ldebug ("core->decr %d live %d core->live %d\n",
(HWVoiceOut *hw, void *buf, size_t size), core->decr,
(hw, buf, size)) live,
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), core->live);
(hw, buf, size)) }
#undef COREAUDIO_WRAPPER_FUNC
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 */ /* callback to feed audiooutput buffer */
static OSStatus audioDeviceIOProc( static OSStatus audioDeviceIOProc(
@@ -428,11 +438,19 @@ static OSStatus audioDeviceIOProc(
const AudioTimeStamp* inOutputTime, const AudioTimeStamp* inOutputTime,
void* hwptr) void* hwptr)
{ {
UInt32 frameCount, pending_frames; UInt32 frame, frameCount;
void *out = outOutputData->mBuffers[0].mData; float *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr; HWVoiceOut *hw = hwptr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) 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")) { if (coreaudio_lock (core, "audioDeviceIOProc")) {
inInputTime = 0; inInputTime = 0;
@@ -440,37 +458,47 @@ static OSStatus audioDeviceIOProc(
} }
frameCount = core->audioDevicePropertyBufferFrameSize; 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 there are not enough samples, set signal and return */
if (pending_frames < frameCount) { if (live < frameCount) {
inInputTime = 0; inInputTime = 0;
coreaudio_unlock (core, "audioDeviceIOProc(empty)"); coreaudio_unlock (core, "audioDeviceIOProc(empty)");
return 0; return 0;
} }
len = frameCount * hw->info.bytes_per_frame; rpos = core->rpos;
while (len) { src = hw->mix_buf + rpos;
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);
write_len = MIN(MIN(hw->pending_emul, len), /* fill buffer */
hw->size_emul - start); for (frame = 0; frame < frameCount; frame++) {
#ifdef FLOAT_MIXENG
memcpy(out, hw->buf_emul + start, write_len); *out++ = src[frame].l; /* left channel */
hw->pending_emul -= write_len; *out++ = src[frame].r; /* right channel */
len -= write_len; #else
out += write_len; #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"); coreaudio_unlock (core, "audioDeviceIOProc");
return 0; 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, static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque) void *drv_opaque)
{ {
@@ -479,10 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
int err; int err;
const char *typ = "playback"; const char *typ = "playback";
AudioValueRange frameRange; AudioValueRange frameRange;
Audiodev *dev = drv_opaque; CoreaudioConf *conf = drv_opaque;
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
int frames;
struct audsettings fake_as;
/* create mutex */ /* create mutex */
err = pthread_mutex_init(&core->mutex, NULL); err = pthread_mutex_init(&core->mutex, NULL);
@@ -491,9 +516,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1; return -1;
} }
fake_as = *as;
as = &fake_as;
as->fmt = AUDIO_FORMAT_F32;
audio_pcm_init_info (&hw->info, as); audio_pcm_init_info (&hw->info, as);
status = coreaudio_get_voice(&core->outputDeviceID); status = coreaudio_get_voice(&core->outputDeviceID);
@@ -516,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1; return -1;
} }
frames = audio_buffer_frames( if (frameRange.mMinimum > conf->buffer_frames) {
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
if (frameRange.mMinimum > frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
} else if (frameRange.mMaximum < frames) { }
else if (frameRange.mMaximum < conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
} }
else { else {
core->audioDevicePropertyBufferFrameSize = frames; core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
} }
/* set Buffer Frame Size */ /* set Buffer Frame Size */
@@ -547,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
"Could not get device buffer frame size\n"); "Could not get device buffer frame size\n");
return -1; return -1;
} }
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */ /* get StreamFormat */
status = coreaudio_get_streamformat(core->outputDeviceID, status = coreaudio_get_streamformat(core->outputDeviceID,
@@ -562,7 +582,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
/* set Samplerate */ /* set Samplerate */
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
status = coreaudio_set_streamformat(core->outputDeviceID, status = coreaudio_set_streamformat(core->outputDeviceID,
&core->outputStreamBasicDescription); &core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) { if (status != kAudioHardwareNoError) {
@@ -629,12 +648,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; OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (enable) { switch (cmd) {
case VOICE_ENABLE:
/* start playback */ /* start playback */
if (!isPlaying(core->outputDeviceID)) { if (!isPlaying(core->outputDeviceID)) {
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
@@ -642,7 +662,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
coreaudio_logerr (status, "Could not resume playback\n"); coreaudio_logerr (status, "Could not resume playback\n");
} }
} }
} else { break;
case VOICE_DISABLE:
/* stop playback */ /* stop playback */
if (!audio_is_cleaning_up()) { if (!audio_is_cleaning_up()) {
if (isPlaying(core->outputDeviceID)) { if (isPlaying(core->outputDeviceID)) {
@@ -653,33 +675,57 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
} }
} }
} }
break;
} }
return 0;
} }
static void *coreaudio_audio_init(Audiodev *dev) static CoreaudioConf glob_conf = {
.buffer_frames = 512,
.nbuffers = 4,
};
static void *coreaudio_audio_init (void)
{ {
return dev; CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
*conf = glob_conf;
return conf;
} }
static void coreaudio_audio_fini (void *opaque) static void coreaudio_audio_fini (void *opaque)
{ {
g_free(opaque);
} }
static struct audio_option coreaudio_options[] = {
{
.name = "BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_frames,
.descr = "Size of the buffer in frames"
},
{
.name = "BUFFER_COUNT",
.tag = AUD_OPT_INT,
.valp = &glob_conf.nbuffers,
.descr = "Number of buffers"
},
{ /* End of list */ }
};
static struct audio_pcm_ops coreaudio_pcm_ops = { static struct audio_pcm_ops coreaudio_pcm_ops = {
.init_out = coreaudio_init_out, .init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out, .fini_out = coreaudio_fini_out,
/* wrapper for audio_generic_write */ .run_out = coreaudio_run_out,
.write = coreaudio_write, .write = coreaudio_write,
/* wrapper for audio_generic_get_buffer_out */ .ctl_out = coreaudio_ctl_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
}; };
static struct audio_driver coreaudio_audio_driver = { static struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio", .name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
.options = coreaudio_options,
.init = coreaudio_audio_init, .init = coreaudio_audio_init,
.fini = coreaudio_audio_fini, .fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops, .pcm_ops = &coreaudio_pcm_ops,

View File

@@ -29,8 +29,6 @@
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
#define FIELD dsound_capture_buffer #define FIELD dsound_capture_buffer
#define FIELD2 dsound_capture #define FIELD2 dsound_capture
#define HWVOICE HWVoiceIn
#define DSOUNDVOICE DSoundVoiceIn
#else #else
#define NAME "playback buffer" #define NAME "playback buffer"
#define NAME2 "DirectSound" #define NAME2 "DirectSound"
@@ -39,8 +37,6 @@
#define BUFPTR LPDIRECTSOUNDBUFFER #define BUFPTR LPDIRECTSOUNDBUFFER
#define FIELD dsound_buffer #define FIELD dsound_buffer
#define FIELD2 dsound #define FIELD2 dsound
#define HWVOICE HWVoiceOut
#define DSOUNDVOICE DSoundVoiceOut
#endif #endif
static int glue (dsound_unlock_, TYPE) ( static int glue (dsound_unlock_, TYPE) (
@@ -76,6 +72,8 @@ static int glue (dsound_lock_, TYPE) (
) )
{ {
HRESULT hr; HRESULT hr;
LPVOID p1 = NULL, p2 = NULL;
DWORD blen1 = 0, blen2 = 0;
DWORD flag; DWORD flag;
#ifdef DSBTYPE_IN #ifdef DSBTYPE_IN
@@ -83,7 +81,7 @@ static int glue (dsound_lock_, TYPE) (
#else #else
flag = entire ? DSBLOCK_ENTIREBUFFER : 0; flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
#endif #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)) { if (FAILED (hr)) {
#ifndef DSBTYPE_IN #ifndef DSBTYPE_IN
@@ -98,34 +96,34 @@ static int glue (dsound_lock_, TYPE) (
goto fail; goto fail;
} }
if ((p1p && *p1p && (*blen1p % info->bytes_per_frame)) || if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
(p2p && *p2p && (*blen2p % info->bytes_per_frame))) { dolog ("DirectSound returned misaligned buffer %ld %ld\n",
dolog("DirectSound returned misaligned buffer %ld %ld\n", blen1, blen2);
*blen1p, *blen2p); glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
glue(dsound_unlock_, TYPE)(buf, *p1p, p2p ? *p2p : NULL, *blen1p,
blen2p ? *blen2p : 0);
goto fail; goto fail;
} }
if (p1p && !*p1p && *blen1p) { if (!p1 && blen1) {
dolog("warning: !p1 && blen1=%ld\n", *blen1p); dolog ("warning: !p1 && blen1=%ld\n", blen1);
*blen1p = 0; blen1 = 0;
} }
if (p2p && !*p2p && *blen2p) { if (!p2 && blen2) {
dolog("warning: !p2 && blen2=%ld\n", *blen2p); dolog ("warning: !p2 && blen2=%ld\n", blen2);
*blen2p = 0; blen2 = 0;
} }
*p1p = p1;
*p2p = p2;
*blen1p = blen1;
*blen2p = blen2;
return 0; return 0;
fail: fail:
*p1p = NULL - 1; *p1p = NULL - 1;
*p2p = NULL - 1;
*blen1p = -1; *blen1p = -1;
if (p2p) { *blen2p = -1;
*p2p = NULL - 1;
*blen2p = -1;
}
return -1; return -1;
} }
@@ -169,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
dsound *s = drv_opaque; dsound *s = drv_opaque;
WAVEFORMATEX wfx; WAVEFORMATEX wfx;
struct audsettings obt_as; struct audsettings obt_as;
DSoundConf *conf = &s->conf;
#ifdef DSBTYPE_IN #ifdef DSBTYPE_IN
const char *typ = "ADC"; const char *typ = "ADC";
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
DSCBUFFERDESC bd; DSCBUFFERDESC bd;
DSCBCAPS bc; DSCBCAPS bc;
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
#else #else
const char *typ = "DAC"; const char *typ = "DAC";
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
DSBUFFERDESC bd; DSBUFFERDESC bd;
DSBCAPS bc; DSBCAPS bc;
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
#endif #endif
if (!s->FIELD2) { if (!s->FIELD2) {
@@ -196,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
memset (&bd, 0, sizeof (bd)); memset (&bd, 0, sizeof (bd));
bd.dwSize = sizeof (bd); bd.dwSize = sizeof (bd);
bd.lpwfxFormat = &wfx; bd.lpwfxFormat = &wfx;
bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
#ifdef DSBTYPE_IN #ifdef DSBTYPE_IN
bd.dwBufferBytes = conf->bufsize_in;
hr = IDirectSoundCapture_CreateCaptureBuffer ( hr = IDirectSoundCapture_CreateCaptureBuffer (
s->dsound_capture, s->dsound_capture,
&bd, &bd,
@@ -206,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
); );
#else #else
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
bd.dwBufferBytes = conf->bufsize_out;
hr = IDirectSound_CreateSoundBuffer ( hr = IDirectSound_CreateSoundBuffer (
s->dsound, s->dsound,
&bd, &bd,
@@ -244,23 +242,25 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
goto fail0; goto fail0;
} }
ds->first_time = true; ds->first_time = 1;
obt_as.endianness = 0; obt_as.endianness = 0;
audio_pcm_init_info (&hw->info, &obt_as); audio_pcm_init_info (&hw->info, &obt_as);
if (bc.dwBufferBytes % hw->info.bytes_per_frame) { if (bc.dwBufferBytes & hw->info.align) {
dolog ( dolog (
"GetCaps returned misaligned buffer size %ld, alignment %d\n", "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.shift;
hw->samples = bc.dwBufferBytes / hw->info.bytes_per_frame;
ds->s = s; ds->s = s;
#ifdef DEBUG_DSOUND #ifdef DEBUG_DSOUND
dolog ("caps %ld, desc %ld\n", dolog ("caps %ld, desc %ld\n",
bc.dwBufferBytes, bd.dwBufferBytes); bc.dwBufferBytes, bd.dwBufferBytes);
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
#endif #endif
return 0; return 0;
@@ -276,5 +276,3 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
#undef BUFPTR #undef BUFPTR
#undef FIELD #undef FIELD
#undef FIELD2 #undef FIELD2
#undef HWVOICE
#undef DSOUNDVOICE

View File

@@ -27,12 +27,11 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "dsound" #define AUDIO_CAP "dsound"
#include "audio_int.h" #include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
@@ -43,24 +42,36 @@
/* #define DEBUG_DSOUND */ /* #define DEBUG_DSOUND */
typedef struct {
int bufsize_in;
int bufsize_out;
int latency_millis;
} DSoundConf;
typedef struct { typedef struct {
LPDIRECTSOUND dsound; LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture; LPDIRECTSOUNDCAPTURE dsound_capture;
struct audsettings settings; struct audsettings settings;
Audiodev *dev; DSoundConf conf;
} dsound; } dsound;
typedef struct { typedef struct {
HWVoiceOut hw; HWVoiceOut hw;
LPDIRECTSOUNDBUFFER dsound_buffer; LPDIRECTSOUNDBUFFER dsound_buffer;
bool first_time; DWORD old_pos;
int first_time;
dsound *s; dsound *s;
#ifdef DEBUG_DSOUND
DWORD old_ppos;
DWORD played;
DWORD mixed;
#endif
} DSoundVoiceOut; } DSoundVoiceOut;
typedef struct { typedef struct {
HWVoiceIn hw; HWVoiceIn hw;
int first_time;
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
bool first_time;
dsound *s; dsound *s;
} DSoundVoiceIn; } DSoundVoiceIn;
@@ -237,6 +248,11 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
dsound_log_hresult (hr); dsound_log_hresult (hr);
} }
static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
{
return (millis * info->bytes_per_second) / 1000;
}
#ifdef DEBUG_DSOUND #ifdef DEBUG_DSOUND
static void print_wave_format (WAVEFORMATEX *wfx) static void print_wave_format (WAVEFORMATEX *wfx)
{ {
@@ -279,7 +295,7 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
return -1; return -1;
} }
if (*statusp & DSBSTATUS_BUFFERLOST) { if (*statusp & DSERR_BUFFERLOST) {
dsound_restore_out(dsb, s); dsound_restore_out(dsb, s);
return -1; return -1;
} }
@@ -301,6 +317,33 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
return 0; 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, static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsound *s) dsound *s)
{ {
@@ -312,7 +355,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsb, dsb,
&hw->info, &hw->info,
0, 0,
hw->size_emul, hw->samples << hw->info.shift,
&p1, &p2, &p1, &p2,
&blen1, &blen2, &blen1, &blen2,
1, 1,
@@ -322,8 +365,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
return; return;
} }
len1 = blen1 / hw->info.bytes_per_frame; len1 = blen1 >> hw->info.shift;
len2 = blen2 / hw->info.bytes_per_frame; len2 = blen2 >> hw->info.shift;
#ifdef DEBUG_DSOUND #ifdef DEBUG_DSOUND
dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
@@ -363,7 +406,7 @@ static int dsound_open (dsound *s)
return 0; return 0;
} }
static void dsound_enable_out(HWVoiceOut *hw, bool enable) static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
{ {
HRESULT hr; HRESULT hr;
DWORD status; DWORD status;
@@ -373,17 +416,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
if (!dsb) { if (!dsb) {
dolog ("Attempt to control voice without a buffer\n"); 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)) { if (dsound_get_status_out (dsb, &status, s)) {
return; return -1;
} }
if (status & DSBSTATUS_PLAYING) { if (status & DSBSTATUS_PLAYING) {
dolog ("warning: Voice is already playing\n"); dolog ("warning: Voice is already playing\n");
return; return 0;
} }
dsound_clear_sample (hw, dsb, s); dsound_clear_sample (hw, dsb, s);
@@ -391,85 +435,170 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not start playing buffer\n"); 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)) { if (dsound_get_status_out (dsb, &status, s)) {
return; return -1;
} }
if (status & DSBSTATUS_PLAYING) { if (status & DSBSTATUS_PLAYING) {
hr = IDirectSoundBuffer_Stop (dsb); hr = IDirectSoundBuffer_Stop (dsb);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop playing buffer\n"); dsound_logerr (hr, "Could not stop playing buffer\n");
return; return -1;
} }
} }
else { else {
dolog ("warning: Voice is not playing\n"); 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; int err;
void *ret; HRESULT hr;
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)
{
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; 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;
DSoundConf *conf = &s->conf;
if (err) { if (!dsb) {
dolog("Failed to unlock buffer!!\n"); dolog ("Attempt to run empty with playback buffer\n");
return 0; 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 (conf->latency_millis) {
DWORD cur_blat;
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
ds->first_time = 0;
old_pos = wpos;
old_pos +=
millis_to_bytes (&hw->info, conf->latency_millis) - 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; HRESULT hr;
DWORD status; DWORD status;
@@ -478,17 +607,18 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
if (!dscb) { if (!dscb) {
dolog ("Attempt to control capture voice without a buffer\n"); 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)) { if (dsound_get_status_in (dscb, &status)) {
return; return -1;
} }
if (status & DSCBSTATUS_CAPTURING) { if (status & DSCBSTATUS_CAPTURING) {
dolog ("warning: Voice is already capturing\n"); dolog ("warning: Voice is already capturing\n");
return; return 0;
} }
/* clear ?? */ /* clear ?? */
@@ -496,81 +626,132 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not start capturing\n"); dsound_logerr (hr, "Could not start capturing\n");
return; return -1;
} }
} else { break;
case VOICE_DISABLE:
if (dsound_get_status_in (dscb, &status)) { if (dsound_get_status_in (dscb, &status)) {
return; return -1;
} }
if (status & DSCBSTATUS_CAPTURING) { if (status & DSCBSTATUS_CAPTURING) {
hr = IDirectSoundCaptureBuffer_Stop (dscb); hr = IDirectSoundCaptureBuffer_Stop (dscb);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop capturing\n"); dsound_logerr (hr, "Could not stop capturing\n");
return; return -1;
} }
} }
else { else {
dolog ("warning: Voice is not capturing\n"); 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; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
HRESULT hr; int live, len, dead;
DWORD cpos, rpos, act_size; DWORD blen1, blen2;
size_t req_size; DWORD len1, len2;
int err; DWORD decr;
void *ret; DWORD cpos, rpos;
LPVOID p1, p2;
int hwshift;
dsound *s = ds->s;
hr = IDirectSoundCaptureBuffer_GetCurrentPosition( if (!dscb) {
dscb, &cpos, ds->first_time ? &rpos : NULL); dolog ("Attempt to run without capture buffer\n");
if (FAILED(hr)) { return 0;
dsound_logerr(hr, "Could not get capture buffer position\n"); }
*size = 0;
return NULL; 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) { if (ds->first_time) {
hw->pos_emul = rpos; ds->first_time = 0;
ds->first_time = false; 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); if (cpos & hw->info.align) {
req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul)); ldebug ("warning: Misaligned capture position %ld(%d)\n",
cpos, hw->info.align);
if (req_size == 0) {
*size = 0;
return NULL;
} }
cpos >>= hwshift;
err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL, len = audio_ring_dist (cpos, hw->wpos, hw->samples);
&act_size, NULL, false, ds->s); 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) { if (err) {
dolog("Failed to lock buffer\n"); return 0;
*size = 0;
return NULL;
} }
*size = act_size; len1 = blen1 >> hwshift;
return ret; len2 = blen2 >> hwshift;
decr = len1 + len2;
if (p1 && len1) {
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
}
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_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len) static DSoundConf glob_conf = {
{ .bufsize_in = 16384,
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; .bufsize_out = 16384,
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; .latency_millis = 10
int err = dsound_unlock_in(dscb, buf, NULL, len, 0); };
if (err) {
dolog("Failed to unlock buffer!!\n");
return;
}
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
}
static void dsound_audio_fini (void *opaque) static void dsound_audio_fini (void *opaque)
{ {
@@ -602,22 +783,13 @@ static void dsound_audio_fini (void *opaque)
g_free(s); g_free(s);
} }
static void *dsound_audio_init(Audiodev *dev) static void *dsound_audio_init (void)
{ {
int err; int err;
HRESULT hr; HRESULT hr;
dsound *s = g_malloc0(sizeof(dsound)); dsound *s = g_malloc0(sizeof(dsound));
AudiodevDsoundOptions *dso;
assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
s->dev = dev;
dso = &dev->u.dsound;
if (!dso->has_latency) {
dso->has_latency = true;
dso->latency = 10000; /* 10 ms */
}
s->conf = glob_conf;
hr = CoInitialize (NULL); hr = CoInitialize (NULL);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n"); dsound_logerr (hr, "Could not initialize COM\n");
@@ -682,25 +854,46 @@ static void *dsound_audio_init(Audiodev *dev)
return s; return s;
} }
static struct audio_option dsound_options[] = {
{
.name = "LATENCY_MILLIS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.latency_millis,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_OUT",
.tag = AUD_OPT_INT,
.valp = &glob_conf.bufsize_out,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_IN",
.tag = AUD_OPT_INT,
.valp = &glob_conf.bufsize_in,
.descr = "(undocumented)"
},
{ /* End of list */ }
};
static struct audio_pcm_ops dsound_pcm_ops = { static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out, .init_out = dsound_init_out,
.fini_out = dsound_fini_out, .fini_out = dsound_fini_out,
.write = audio_generic_write, .run_out = dsound_run_out,
.get_buffer_out = dsound_get_buffer_out, .write = dsound_write,
.put_buffer_out = dsound_put_buffer_out, .ctl_out = dsound_ctl_out,
.enable_out = dsound_enable_out,
.init_in = dsound_init_in, .init_in = dsound_init_in,
.fini_in = dsound_fini_in, .fini_in = dsound_fini_in,
.read = audio_generic_read, .run_in = dsound_run_in,
.get_buffer_in = dsound_get_buffer_in, .read = dsound_read,
.put_buffer_in = dsound_put_buffer_in, .ctl_in = dsound_ctl_in
.enable_in = dsound_enable_in,
}; };
static struct audio_driver dsound_audio_driver = { static struct audio_driver dsound_audio_driver = {
.name = "dsound", .name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound", .descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.options = dsound_options,
.init = dsound_audio_init, .init = dsound_audio_init,
.fini = dsound_audio_fini, .fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops, .pcm_ops = &dsound_pcm_ops,

View File

@@ -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);

View File

@@ -23,6 +23,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "audio.h" #include "audio.h"
@@ -267,82 +268,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 void audio_sample_to_uint64(void *samples, int pos,
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,
uint64_t *left, uint64_t *right) uint64_t *left, uint64_t *right)
{ {
const struct st_sample *sample = samples; struct st_sample *sample = samples;
sample += pos; sample += pos;
#ifdef FLOAT_MIXENG #ifdef FLOAT_MIXENG
error_report( error_report(

View File

@@ -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 mixeng_volume { int mute; int64_t r; int64_t l; };
struct st_sample { int64_t l; int64_t r; }; struct st_sample { int64_t l; int64_t r; };
#endif #endif
typedef struct st_sample st_sample;
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
/* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */
extern t_sample *mixeng_conv[2][2][2][3]; extern t_sample *mixeng_conv[2][2][2][3];
extern f_sample *mixeng_clip[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_start (int inrate, int outrate);
void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
size_t *isamp, size_t *osamp); int *isamp, int *osamp);
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf, void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
size_t *isamp, size_t *osamp); int *isamp, int *osamp);
void st_rate_stop (void *opaque); void st_rate_stop (void *opaque);
void mixeng_clear (struct st_sample *buf, int len); void mixeng_clear (struct st_sample *buf, int len);
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);

View File

@@ -41,31 +41,32 @@ static inline mixeng_real glue (conv_, ET) (IN_T v)
#ifdef RECIPROCAL #ifdef RECIPROCAL
#ifdef SIGNED #ifdef SIGNED
return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN)); return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN));
#else #else
return (nv - HALF) * (2.f / (mixeng_real)IN_MAX); return (nv - HALF) * (1.f / (mixeng_real) IN_MAX);
#endif #endif
#else /* !RECIPROCAL */ #else /* !RECIPROCAL */
#ifdef SIGNED #ifdef SIGNED
return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f); return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN);
#else #else
return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f); return (nv - HALF) / (mixeng_real) IN_MAX;
#endif #endif
#endif #endif
} }
static inline IN_T glue (clip_, ET) (mixeng_real v) static inline IN_T glue (clip_, ET) (mixeng_real v)
{ {
if (v >= 1.f) { if (v >= 0.5) {
return IN_MAX; return IN_MAX;
} else if (v < -1.f) { }
else if (v < -0.5) {
return IN_MIN; return IN_MIN;
} }
#ifdef SIGNED #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 #else
return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF)); return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
#endif #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) static inline IN_T glue (clip_, ET) (int64_t v)
{ {
if (v >= 0x7fffffffLL) { if (v >= 0x7f000000) {
return IN_MAX; return IN_MAX;
} else if (v < -2147483648LL) { }
else if (v < -2147483648LL) {
return IN_MIN; return IN_MIN;
} }

View File

@@ -21,10 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qemu/module.h"
#include "audio.h" #include "audio.h"
#include "qemu/timer.h" #include "qemu/timer.h"
@@ -33,27 +32,43 @@
typedef struct NoVoiceOut { typedef struct NoVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
RateCtl rate; int64_t old_ticks;
} NoVoiceOut; } NoVoiceOut;
typedef struct NoVoiceIn { typedef struct NoVoiceIn {
HWVoiceIn hw; HWVoiceIn hw;
RateCtl rate; int64_t old_ticks;
} NoVoiceIn; } 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; 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) static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{ {
NoVoiceOut *no = (NoVoiceOut *) hw;
audio_pcm_init_info (&hw->info, as); audio_pcm_init_info (&hw->info, as);
hw->samples = 1024; hw->samples = 1024;
audio_rate_start(&no->rate);
return 0; return 0;
} }
@@ -62,22 +77,17 @@ static void no_fini_out (HWVoiceOut *hw)
(void) 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; (void) hw;
(void) cmd;
if (enable) { return 0;
audio_rate_start(&no->rate);
}
} }
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{ {
NoVoiceIn *no = (NoVoiceIn *) hw;
audio_pcm_init_info (&hw->info, as); audio_pcm_init_info (&hw->info, as);
hw->samples = 1024; hw->samples = 1024;
audio_rate_start(&no->rate);
return 0; return 0;
} }
@@ -86,25 +96,47 @@ static void no_fini_in (HWVoiceIn *hw)
(void) 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; 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); if (dead) {
return bytes; 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);
static void no_enable_in(HWVoiceIn *hw, bool enable) no->old_ticks = now;
{ bytes = audio_MIN (bytes, INT_MAX);
NoVoiceIn *no = (NoVoiceIn *) hw; samples = bytes >> hw->info.shift;
samples = audio_MIN (samples, dead);
if (enable) {
audio_rate_start(&no->rate);
} }
return samples;
} }
static void *no_audio_init(Audiodev *dev) static int no_read (SWVoiceIn *sw, void *buf, int size)
{
/* 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;
}
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
return 0;
}
static void *no_audio_init (void)
{ {
return &no_audio_init; return &no_audio_init;
} }
@@ -117,19 +149,21 @@ static void no_audio_fini (void *opaque)
static struct audio_pcm_ops no_pcm_ops = { static struct audio_pcm_ops no_pcm_ops = {
.init_out = no_init_out, .init_out = no_init_out,
.fini_out = no_fini_out, .fini_out = no_fini_out,
.run_out = no_run_out,
.write = no_write, .write = no_write,
.run_buffer_out = audio_generic_run_buffer_out, .ctl_out = no_ctl_out,
.enable_out = no_enable_out,
.init_in = no_init_in, .init_in = no_init_in,
.fini_in = no_fini_in, .fini_in = no_fini_in,
.run_in = no_run_in,
.read = no_read, .read = no_read,
.enable_in = no_enable_in .ctl_in = no_ctl_in
}; };
static struct audio_driver no_audio_driver = { static struct audio_driver no_audio_driver = {
.name = "none", .name = "none",
.descr = "Timer based audio emulation", .descr = "Timer based audio emulation",
.options = NULL,
.init = no_audio_init, .init = no_audio_init,
.fini = no_audio_fini, .fini = no_audio_fini,
.pcm_ops = &no_pcm_ops, .pcm_ops = &no_pcm_ops,

View File

@@ -21,12 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/soundcard.h> #include <sys/soundcard.h>
#include "qemu-common.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "audio.h" #include "audio.h"
#include "trace.h" #include "trace.h"
@@ -38,26 +37,40 @@
#define USE_DSP_POLICY #define USE_DSP_POLICY
#endif #endif
typedef struct OSSConf {
int try_mmap;
int nfrags;
int fragsize;
const char *devpath_out;
const char *devpath_in;
int exclusive;
int policy;
} OSSConf;
typedef struct OSSVoiceOut { typedef struct OSSVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
void *pcm_buf;
int fd; int fd;
int wpos;
int nfrags; int nfrags;
int fragsize; int fragsize;
int mmapped; int mmapped;
Audiodev *dev; int pending;
OSSConf *conf;
} OSSVoiceOut; } OSSVoiceOut;
typedef struct OSSVoiceIn { typedef struct OSSVoiceIn {
HWVoiceIn hw; HWVoiceIn hw;
void *pcm_buf;
int fd; int fd;
int nfrags; int nfrags;
int fragsize; int fragsize;
Audiodev *dev; OSSConf *conf;
} OSSVoiceIn; } OSSVoiceIn;
struct oss_params { struct oss_params {
int freq; int freq;
int fmt; audfmt_e fmt;
int nchannels; int nchannels;
int nfrags; int nfrags;
int fragsize; int fragsize;
@@ -106,40 +119,45 @@ static void oss_anal_close (int *fdp)
static void oss_helper_poll_out (void *opaque) static void oss_helper_poll_out (void *opaque)
{ {
AudioState *s = opaque; (void) opaque;
audio_run(s, "oss_poll_out"); audio_run ("oss_poll_out");
} }
static void oss_helper_poll_in (void *opaque) static void oss_helper_poll_in (void *opaque)
{ {
AudioState *s = opaque; (void) opaque;
audio_run(s, "oss_poll_in"); audio_run ("oss_poll_in");
} }
static void oss_poll_out (HWVoiceOut *hw) static void oss_poll_out (HWVoiceOut *hw)
{ {
OSSVoiceOut *oss = (OSSVoiceOut *) 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) static void oss_poll_in (HWVoiceIn *hw)
{ {
OSSVoiceIn *oss = (OSSVoiceIn *) 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 aud_to_ossfmt (AudioFormat fmt, int endianness) static int oss_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int aud_to_ossfmt (audfmt_e fmt, int endianness)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
return AFMT_S8; return AFMT_S8;
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
return AFMT_U8; return AFMT_U8;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
if (endianness) { if (endianness) {
return AFMT_S16_BE; return AFMT_S16_BE;
} }
@@ -147,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
return AFMT_S16_LE; return AFMT_S16_LE;
} }
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
if (endianness) { if (endianness) {
return AFMT_U16_BE; return AFMT_U16_BE;
} }
@@ -164,37 +182,37 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
} }
} }
static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness) static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
{ {
switch (ossfmt) { switch (ossfmt) {
case AFMT_S8: case AFMT_S8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S8; *fmt = AUD_FMT_S8;
break; break;
case AFMT_U8: case AFMT_U8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U8; *fmt = AUD_FMT_U8;
break; break;
case AFMT_S16_LE: case AFMT_S16_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AFMT_U16_LE: case AFMT_U16_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
case AFMT_S16_BE: case AFMT_S16_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AFMT_U16_BE: case AFMT_U16_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
default: default:
@@ -244,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ)
} }
#endif #endif
static int oss_open(int in, struct oss_params *req, audsettings *as, static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd, Audiodev *dev) struct oss_params *obt, int *pfd, OSSConf* conf)
{ {
AudiodevOssOptions *oopts = &dev->u.oss;
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
int fd; int fd;
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; int oflags = conf->exclusive ? O_EXCL : 0;
audio_buf_info abinfo; audio_buf_info abinfo;
int fmt, freq, nchannels; int fmt, freq, nchannels;
int setfragment = 1; int setfragment = 1;
const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; const char *dspname = in ? conf->devpath_in : conf->devpath_out;
const char *typ = in ? "ADC" : "DAC"; const char *typ = in ? "ADC" : "DAC";
#ifdef USE_DSP_POLICY
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
#endif
/* Kludge needed to have working mmap on Linux */ /* Kludge needed to have working mmap on Linux */
oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK); fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) { if (-1 == fd) {
@@ -273,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
freq = req->freq; freq = req->freq;
nchannels = req->nchannels; nchannels = req->nchannels;
fmt = req->fmt; fmt = req->fmt;
req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
req->fragsize = audio_buffer_bytes(
qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
@@ -299,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
} }
#ifdef USE_DSP_POLICY #ifdef USE_DSP_POLICY
if (policy >= 0) { if (conf->policy >= 0) {
int version; int version;
if (!oss_get_version (fd, &version, typ)) { if (!oss_get_version (fd, &version, typ)) {
trace_oss_version(version); trace_oss_version(version);
if (version >= 0x040000) { if (version >= 0x040000) {
int policy2 = policy; int policy = conf->policy;
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
oss_logerr2 (errno, typ, oss_logerr2 (errno, typ,
"Failed to set timing policy to %d\n", "Failed to set timing policy to %d\n",
policy); conf->policy);
goto err; goto err;
} }
setfragment = 0; setfragment = 0;
@@ -367,96 +376,97 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
return -1; return -1;
} }
static size_t oss_get_available_bytes(OSSVoiceOut *oss) static void oss_write_pending (OSSVoiceOut *oss)
{ {
int err; HWVoiceOut *hw = &oss->hw;
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;
if (oss->mmapped) { if (oss->mmapped) {
size_t total_len; return;
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;
} }
pos = 0; while (oss->pending) {
while (len) { int samples_written;
ssize_t bytes_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 (bytes_written < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
oss_logerr(errno, "failed to write %zu bytes\n", oss_logerr (errno, "failed to write %d bytes\n",
len); bytes_to_write);
} }
return pos;
}
pos += bytes_written;
if (bytes_written < len) {
break; 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) static void oss_fini_out (HWVoiceOut *hw)
@@ -467,13 +477,18 @@ static void oss_fini_out (HWVoiceOut *hw)
ldebug ("oss_fini\n"); ldebug ("oss_fini\n");
oss_anal_close (&oss->fd); oss_anal_close (&oss->fd);
if (oss->mmapped && hw->buf_emul) { if (oss->pcm_buf) {
err = munmap(hw->buf_emul, hw->size_emul); if (oss->mmapped) {
if (err) { err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n", if (err) {
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; else {
g_free (oss->pcm_buf);
}
oss->pcm_buf = NULL;
} }
} }
@@ -485,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
int endianness; int endianness;
int err; int err;
int fd; int fd;
AudioFormat effective_fmt; audfmt_e effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; OSSConf *conf = drv_opaque;
AudiodevOssOptions *oopts = &dev->u.oss;
oss->fd = -1; oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
req.fragsize = conf->fragsize;
req.nfrags = conf->nfrags;
if (oss_open(0, &req, as, &obt, &fd, dev)) { if (oss_open (0, &req, &obt, &fd, conf)) {
return -1; return -1;
} }
@@ -515,29 +531,28 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
oss->nfrags = obt.nfrags; oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize; 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", 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; oss->mmapped = 0;
if (oopts->has_try_mmap && oopts->try_mmap) { if (conf->try_mmap) {
hw->size_emul = hw->samples * hw->info.bytes_per_frame; oss->pcm_buf = mmap (
hw->buf_emul = mmap(
NULL, NULL,
hw->size_emul, hw->samples << hw->info.shift,
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_SHARED, MAP_SHARED,
fd, fd,
0 0
); );
if (hw->buf_emul == MAP_FAILED) { if (oss->pcm_buf == MAP_FAILED) {
oss_logerr(errno, "Failed to map %zu bytes of DAC\n", oss_logerr (errno, "Failed to map %d bytes of DAC\n",
hw->size_emul); hw->samples << hw->info.shift);
hw->buf_emul = NULL; }
} else { else {
int err; int err;
int trig = 0; int trig = 0;
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@@ -557,63 +572,92 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
} }
if (!oss->mmapped) { if (!oss->mmapped) {
err = munmap(hw->buf_emul, hw->size_emul); err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) { if (err) {
oss_logerr(errno, "Failed to unmap buffer %p size %zu\n", oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
hw->buf_emul, hw->size_emul); 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->fd = fd;
oss->dev = dev; oss->conf = conf;
return 0; return 0;
} }
static void oss_enable_out(HWVoiceOut *hw, bool enable) static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{ {
int trig; int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw; OSSVoiceOut *oss = (OSSVoiceOut *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
if (enable) { switch (cmd) {
hw->poll_mode = opdo->try_poll; case VOICE_ENABLE:
{
va_list ap;
int poll_mode;
ldebug("enabling voice\n"); va_start (ap, cmd);
if (hw->poll_mode) { poll_mode = va_arg (ap, int);
oss_poll_out(hw); va_end (ap);
ldebug ("enabling voice\n");
if (poll_mode) {
oss_poll_out (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
if (!oss->mmapped) {
return 0;
}
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;
}
} }
break;
if (!oss->mmapped) { case VOICE_DISABLE:
return;
}
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;
}
} else {
if (hw->poll_mode) { if (hw->poll_mode) {
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
hw->poll_mode = 0; hw->poll_mode = 0;
} }
if (!oss->mmapped) { if (!oss->mmapped) {
return; return 0;
} }
ldebug ("disabling voice\n"); ldebug ("disabling voice\n");
trig = 0; trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); 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) static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -623,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
int endianness; int endianness;
int err; int err;
int fd; int fd;
AudioFormat effective_fmt; audfmt_e effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; OSSConf *conf = drv_opaque;
oss->fd = -1; oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
if (oss_open(1, &req, as, &obt, &fd, dev)) { req.fragsize = conf->fragsize;
req.nfrags = conf->nfrags;
if (oss_open (1, &req, &obt, &fd, conf)) {
return -1; return -1;
} }
@@ -651,15 +697,22 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
oss->nfrags = obt.nfrags; oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize; 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", 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->fd = fd;
oss->dev = dev; oss->conf = conf;
return 0; return 0;
} }
@@ -668,106 +721,211 @@ static void oss_fini_in (HWVoiceIn *hw)
OSSVoiceIn *oss = (OSSVoiceIn *) hw; OSSVoiceIn *oss = (OSSVoiceIn *) hw;
oss_anal_close (&oss->fd); 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; 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) { if (!dead) {
return 0;
}
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; ssize_t nread;
void *dst = advance(buf, pos); if (bufs[i].len) {
nread = read(oss->fd, dst, len); void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
nread = read (oss->fd, p, bufs[i].len);
if (nread == -1) { if (nread > 0) {
switch (errno) { if (nread & hw->info.align) {
case EINTR: dolog ("warning: Misaligned read %zd (requested %d), "
case EAGAIN: "alignment %d\n", nread, bufs[i].add << hwshift,
break; hw->info.align + 1);
default: }
oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n", read_samples += nread >> hwshift;
len, dst); 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; break;
} }
break;
} }
pos += nread;
len -= nread;
} }
return pos; 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; OSSVoiceIn *oss = (OSSVoiceIn *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
if (enable) { switch (cmd) {
hw->poll_mode = opdo->try_poll; case VOICE_ENABLE:
{
va_list ap;
int poll_mode;
if (hw->poll_mode) { va_start (ap, cmd);
oss_poll_in(hw); poll_mode = va_arg (ap, int);
va_end (ap);
if (poll_mode) {
oss_poll_in (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
} }
} else { break;
case VOICE_DISABLE:
if (hw->poll_mode) { if (hw->poll_mode) {
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
hw->poll_mode = 0; hw->poll_mode = 0;
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
} }
break;
} }
return 0;
} }
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) static OSSConf glob_conf = {
.try_mmap = 0,
.nfrags = 4,
.fragsize = 4096,
.devpath_out = "/dev/dsp",
.devpath_in = "/dev/dsp",
.exclusive = 0,
.policy = 5
};
static void *oss_audio_init (void)
{ {
if (!opdo->has_try_poll) { OSSConf *conf = g_malloc(sizeof(OSSConf));
opdo->try_poll = true; *conf = glob_conf;
opdo->has_try_poll = true;
}
}
static void *oss_audio_init(Audiodev *dev) if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
{ access(conf->devpath_out, R_OK | W_OK) < 0) {
AudiodevOssOptions *oopts; g_free(conf);
assert(dev->driver == AUDIODEV_DRIVER_OSS);
oopts = &dev->u.oss;
oss_init_per_direction(oopts->in);
oss_init_per_direction(oopts->out);
if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
R_OK | W_OK) < 0 ||
access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
R_OK | W_OK) < 0) {
return NULL; return NULL;
} }
return dev; return conf;
} }
static void oss_audio_fini (void *opaque) static void oss_audio_fini (void *opaque)
{ {
g_free(opaque);
} }
static struct audio_option oss_options[] = {
{
.name = "FRAGSIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.fragsize,
.descr = "Fragment size in bytes"
},
{
.name = "NFRAGS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.nfrags,
.descr = "Number of fragments"
},
{
.name = "MMAP",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.try_mmap,
.descr = "Try using memory mapped access"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.devpath_out,
.descr = "Path to DAC device"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.devpath_in,
.descr = "Path to ADC device"
},
{
.name = "EXCLUSIVE",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.exclusive,
.descr = "Open device in exclusive mode (vmix won't work)"
},
#ifdef USE_DSP_POLICY
{
.name = "POLICY",
.tag = AUD_OPT_INT,
.valp = &glob_conf.policy,
.descr = "Set the timing policy of the device, -1 to use fragment mode",
},
#endif
{ /* End of list */ }
};
static struct audio_pcm_ops oss_pcm_ops = { static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out, .init_out = oss_init_out,
.fini_out = oss_fini_out, .fini_out = oss_fini_out,
.run_out = oss_run_out,
.write = oss_write, .write = oss_write,
.run_buffer_out = oss_run_buffer_out, .ctl_out = oss_ctl_out,
.get_buffer_out = oss_get_buffer_out,
.put_buffer_out = oss_put_buffer_out,
.enable_out = oss_enable_out,
.init_in = oss_init_in, .init_in = oss_init_in,
.fini_in = oss_fini_in, .fini_in = oss_fini_in,
.run_in = oss_run_in,
.read = oss_read, .read = oss_read,
.enable_in = oss_enable_in .ctl_in = oss_ctl_in
}; };
static struct audio_driver oss_audio_driver = { static struct audio_driver oss_audio_driver = {
.name = "oss", .name = "oss",
.descr = "OSS http://www.opensound.com", .descr = "OSS http://www.opensound.com",
.options = oss_options,
.init = oss_audio_init, .init = oss_audio_init,
.fini = oss_audio_fini, .fini = oss_audio_fini,
.pcm_ops = &oss_pcm_ops, .pcm_ops = &oss_pcm_ops,

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@
* Return number of samples processed. * Return number of samples processed.
*/ */
void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, 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 rate *rate = opaque;
struct st_sample *istart, *iend; struct st_sample *istart, *iend;

View File

@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <SDL.h> #include <SDL.h>
#include <SDL_thread.h> #include <SDL_thread.h>
#include "qemu/module.h" #include "qemu-common.h"
#include "audio.h" #include "audio.h"
#ifndef _WIN32 #ifndef _WIN32
@@ -39,15 +38,31 @@
#define AUDIO_CAP "sdl" #define AUDIO_CAP "sdl"
#include "audio_int.h" #include "audio_int.h"
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
typedef struct SDLVoiceOut { typedef struct SDLVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
int live;
#if USE_SEMAPHORE
int rpos;
#endif
int decr;
} SDLVoiceOut; } SDLVoiceOut;
static struct {
int nb_samples;
} conf = {
.nb_samples = 1024
};
static struct SDLAudioState { static struct SDLAudioState {
int exit; int exit;
#if USE_SEMAPHORE
SDL_mutex *mutex;
SDL_sem *sem;
#endif
int initialized; int initialized;
bool driver_created; bool driver_created;
Audiodev *dev;
} glob_sdl; } glob_sdl;
typedef struct SDLAudioState SDLAudioState; typedef struct SDLAudioState SDLAudioState;
@@ -62,29 +77,81 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
} }
static int aud_to_sdlfmt (AudioFormat fmt) static int sdl_lock (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_LockMutex (s->mutex)) {
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
return -1;
}
#else
SDL_LockAudio();
#endif
return 0;
}
static int sdl_unlock (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_UnlockMutex (s->mutex)) {
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
return -1;
}
#else
SDL_UnlockAudio();
#endif
return 0;
}
static int sdl_post (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_SemPost (s->sem)) {
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
return -1;
}
#endif
return 0;
}
#if USE_SEMAPHORE
static int sdl_wait (SDLAudioState *s, const char *forfn)
{
if (SDL_SemWait (s->sem)) {
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
return -1;
}
return 0;
}
#endif
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
{
if (sdl_unlock (s, forfn)) {
return -1;
}
return sdl_post (s, forfn);
}
static int aud_to_sdlfmt (audfmt_e fmt)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
return AUDIO_S8; return AUDIO_S8;
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
return AUDIO_U8; return AUDIO_U8;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
return AUDIO_S16LSB; return AUDIO_S16LSB;
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
return AUDIO_U16LSB; 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: default:
dolog ("Internal logic error: Bad audio format %d\n", fmt); dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
@@ -94,57 +161,37 @@ static int aud_to_sdlfmt (AudioFormat fmt)
} }
} }
static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
{ {
switch (sdlfmt) { switch (sdlfmt) {
case AUDIO_S8: case AUDIO_S8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S8; *fmt = AUD_FMT_S8;
break; break;
case AUDIO_U8: case AUDIO_U8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U8; *fmt = AUD_FMT_U8;
break; break;
case AUDIO_S16LSB: case AUDIO_S16LSB:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AUDIO_U16LSB: case AUDIO_U16LSB:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
case AUDIO_S16MSB: case AUDIO_S16MSB:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AUDIO_U16MSB: case AUDIO_U16MSB:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_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; break;
default: default:
@@ -196,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
static void sdl_close (SDLAudioState *s) static void sdl_close (SDLAudioState *s)
{ {
if (s->initialized) { if (s->initialized) {
SDL_LockAudio(); sdl_lock (s, "sdl_close");
s->exit = 1; s->exit = 1;
SDL_UnlockAudio(); sdl_unlock_and_post (s, "sdl_close");
SDL_PauseAudio (1); SDL_PauseAudio (1);
SDL_CloseAudio (); SDL_CloseAudio ();
s->initialized = 0; s->initialized = 0;
@@ -210,59 +257,119 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLVoiceOut *sdl = opaque; SDLVoiceOut *sdl = opaque;
SDLAudioState *s = &glob_sdl; SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw; HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift;
if (s->exit) { if (s->exit) {
return; return;
} }
/* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */ while (samples) {
int to_mix, decr;
while (hw->pending_emul && len) { /* dolog ("in callback samples=%d\n", samples); */
size_t write_len; #if USE_SEMAPHORE
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; sdl_wait (s, "sdl_callback");
if (start < 0) { if (s->exit) {
start += hw->size_emul; return;
} }
assert(start >= 0 && start < hw->size_emul);
write_len = MIN(MIN(hw->pending_emul, len), if (sdl_lock (s, "sdl_callback")) {
hw->size_emul - start); return;
}
memcpy(buf, hw->buf_emul + start, write_len); if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
hw->pending_emul -= write_len; dolog ("sdl->live=%d hw->samples=%d\n",
len -= write_len; sdl->live, hw->samples);
buf += write_len; return;
}
if (!sdl->live) {
goto again;
}
#else
if (s->exit || !sdl->live) {
break;
}
#endif
/* dolog ("in callback live=%d\n", live); */
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;
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip (buf, src, chunk);
#if USE_SEMAPHORE
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
#else
hw->rpos = (hw->rpos + chunk) % hw->samples;
#endif
to_mix -= chunk;
buf += chunk << hw->info.shift;
}
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
#if USE_SEMAPHORE
again:
if (sdl_unlock (s, "sdl_callback")) {
return;
}
#endif
} }
/* dolog ("done len=%d\n", len); */
/* clear remaining buffer that we couldn't fill with data */ #if (SDL_MAJOR_VERSION >= 2)
if (len) { /* SDL2 does not clear the remaining buffer for us, so do it on our own */
memset(buf, 0, len); if (samples) {
memset(buf, 0, samples << hw->info.shift);
} }
#endif
} }
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \ static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
static ret_type glue(sdl_, name)args_decl \ {
{ \ return audio_pcm_sw_write (sw, buf, len);
ret_type ret; \ }
\
SDL_LockAudio(); \ static int sdl_run_out (HWVoiceOut *hw, int live)
\ {
ret = glue(audio_generic_, name)args; \ int decr;
\ SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDL_UnlockAudio(); \ SDLAudioState *s = &glob_sdl;
return ret; \
if (sdl_lock (s, "sdl_run_out")) {
return 0;
} }
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), if (sdl->decr > live) {
(hw, size), *size = 0, sdl_unlock) ldebug ("sdl->decr %d live %d sdl->live %d\n",
SDL_WRAPPER_FUNC(put_buffer_out, size_t, sdl->decr,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), live,
/*nothing*/, sdl_unlock_and_post) sdl->live);
SDL_WRAPPER_FUNC(write, size_t, }
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
#undef SDL_WRAPPER_FUNC decr = audio_MIN (sdl->decr, live);
sdl->decr -= decr;
#if USE_SEMAPHORE
sdl->live = live - decr;
hw->rpos = sdl->rpos;
#else
sdl->live = live;
#endif
if (sdl->live > 0) {
sdl_unlock_and_post (s, "sdl_run_out");
}
else {
sdl_unlock (s, "sdl_run_out");
}
return decr;
}
static void sdl_fini_out (HWVoiceOut *hw) static void sdl_fini_out (HWVoiceOut *hw)
{ {
@@ -279,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
SDL_AudioSpec req, obt; SDL_AudioSpec req, obt;
int endianness; int endianness;
int err; int err;
AudioFormat effective_fmt; audfmt_e effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
req.freq = as->freq; req.freq = as->freq;
req.format = aud_to_sdlfmt (as->fmt); req.format = aud_to_sdlfmt (as->fmt);
req.channels = as->nchannels; req.channels = as->nchannels;
req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); req.samples = conf.nb_samples;
req.callback = sdl_callback; req.callback = sdl_callback;
req.userdata = sdl; req.userdata = sdl;
@@ -313,12 +420,23 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
return 0; 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) static void *sdl_audio_init (void)
{ {
SDLAudioState *s = &glob_sdl; SDLAudioState *s = &glob_sdl;
if (s->driver_created) { if (s->driver_created) {
@@ -331,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev)
return NULL; return NULL;
} }
#if USE_SEMAPHORE
s->mutex = SDL_CreateMutex ();
if (!s->mutex) {
sdl_logerr ("Failed to create SDL mutex\n");
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
s->sem = SDL_CreateSemaphore (0);
if (!s->sem) {
sdl_logerr ("Failed to create SDL semaphore\n");
SDL_DestroyMutex (s->mutex);
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
#endif
s->driver_created = true; s->driver_created = true;
s->dev = dev;
return s; return s;
} }
@@ -340,26 +474,36 @@ static void sdl_audio_fini (void *opaque)
{ {
SDLAudioState *s = opaque; SDLAudioState *s = opaque;
sdl_close (s); sdl_close (s);
#if USE_SEMAPHORE
SDL_DestroySemaphore (s->sem);
SDL_DestroyMutex (s->mutex);
#endif
SDL_QuitSubSystem (SDL_INIT_AUDIO); SDL_QuitSubSystem (SDL_INIT_AUDIO);
s->driver_created = false; s->driver_created = false;
s->dev = NULL;
} }
static struct audio_option sdl_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
.valp = &conf.nb_samples,
.descr = "Size of SDL buffer in samples"
},
{ /* End of list */ }
};
static struct audio_pcm_ops sdl_pcm_ops = { static struct audio_pcm_ops sdl_pcm_ops = {
.init_out = sdl_init_out, .init_out = sdl_init_out,
.fini_out = sdl_fini_out, .fini_out = sdl_fini_out,
/* wrapper for audio_generic_write */ .run_out = sdl_run_out,
.write = sdl_write, .write = sdl_write_out,
/* wrapper for audio_generic_get_buffer_out */ .ctl_out = sdl_ctl_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,
}; };
static struct audio_driver sdl_audio_driver = { static struct audio_driver sdl_audio_driver = {
.name = "sdl", .name = "sdl",
.descr = "SDL http://www.libsdl.org", .descr = "SDL http://www.libsdl.org",
.options = sdl_options,
.init = sdl_audio_init, .init = sdl_audio_init,
.fini = sdl_audio_fini, .fini = sdl_audio_fini,
.pcm_ops = &sdl_pcm_ops, .pcm_ops = &sdl_pcm_ops,

View File

@@ -18,8 +18,8 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "ui/qemu-spice.h" #include "ui/qemu-spice.h"
@@ -40,21 +40,27 @@
#define LINE_IN_SAMPLES (256 * 4) #define LINE_IN_SAMPLES (256 * 4)
#endif #endif
typedef struct SpiceRateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} SpiceRateCtl;
typedef struct SpiceVoiceOut { typedef struct SpiceVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
SpicePlaybackInstance sin; SpicePlaybackInstance sin;
RateCtl rate; SpiceRateCtl rate;
int active; int active;
uint32_t *frame; uint32_t *frame;
uint32_t fpos; uint32_t *fpos;
uint32_t fsize; uint32_t fsize;
} SpiceVoiceOut; } SpiceVoiceOut;
typedef struct SpiceVoiceIn { typedef struct SpiceVoiceIn {
HWVoiceIn hw; HWVoiceIn hw;
SpiceRecordInstance sin; SpiceRecordInstance sin;
RateCtl rate; SpiceRateCtl rate;
int active; int active;
uint32_t samples[LINE_IN_SAMPLES];
} SpiceVoiceIn; } SpiceVoiceIn;
static const SpicePlaybackInterface playback_sif = { static const SpicePlaybackInterface playback_sif = {
@@ -71,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR, .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
}; };
static void *spice_audio_init(Audiodev *dev) static void *spice_audio_init (void)
{ {
if (!using_spice) { if (!using_spice) {
return NULL; return NULL;
@@ -84,6 +90,32 @@ static void spice_audio_fini (void *opaque)
/* nothing */ /* 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 */ /* playback */
static int line_out_init(HWVoiceOut *hw, struct audsettings *as, static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
@@ -98,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
#endif #endif
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
settings.fmt = AUDIO_FORMAT_S16; settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS; settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings); audio_pcm_init_info (&hw->info, &settings);
@@ -120,78 +152,98 @@ static void line_out_fini (HWVoiceOut *hw)
spice_server_remove_interface (&out->sin.base); 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) { if (!live) {
spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize); return 0;
out->fpos = 0;
} }
if (out->frame) { decr = rate_get_samples (&hw->info, &out->rate);
*size = audio_rate_get_bytes( decr = audio_MIN (live, decr);
&hw->info, &out->rate,
(out->fsize - out->fpos) * hw->info.bytes_per_frame); samples = decr;
} else { rpos = hw->rpos;
audio_rate_start(&out->rate); 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); return audio_pcm_sw_write (sw, buf, len);
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;
} }
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); SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
if (enable) { switch (cmd) {
case VOICE_ENABLE:
if (out->active) { if (out->active) {
return; break;
} }
out->active = 1; out->active = 1;
audio_rate_start(&out->rate); rate_start (&out->rate);
spice_server_playback_start (&out->sin); spice_server_playback_start (&out->sin);
} else { break;
case VOICE_DISABLE:
if (!out->active) { if (!out->active) {
return; break;
} }
out->active = 0; out->active = 0;
if (out->frame) { 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); spice_server_playback_put_samples (&out->sin, out->frame);
out->frame = NULL; out->frame = out->fpos = NULL;
} }
spice_server_playback_stop (&out->sin); spice_server_playback_stop (&out->sin);
} break;
} case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
static void line_out_volume(HWVoiceOut *hw, Volume *vol) SWVoiceOut *sw;
{ va_list ap;
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); uint16_t vol[2];
uint16_t svol[2];
assert(vol->channels == 2); va_start (ap, cmd);
svol[0] = vol->vol[0] * 257; sw = va_arg (ap, SWVoiceOut *);
svol[1] = vol->vol[1] * 257; va_end (ap);
spice_server_playback_set_volume(&out->sin, 2, svol);
spice_server_playback_set_mute(&out->sin, vol->mute); 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 #endif
break;
}
}
return 0;
}
/* record */ /* record */
@@ -206,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
settings.freq = SPICE_INTERFACE_RECORD_FREQ; settings.freq = SPICE_INTERFACE_RECORD_FREQ;
#endif #endif
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
settings.fmt = AUDIO_FORMAT_S16; settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS; settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings); audio_pcm_init_info (&hw->info, &settings);
@@ -228,79 +280,121 @@ static void line_in_fini (HWVoiceIn *hw)
spice_server_remove_interface (&in->sin.base); 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); SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2; int num_samples;
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read); int ready;
int len[2];
uint64_t delta_samp;
const uint32_t *samples;
/* XXX: do we need this? */ if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
if (ready == 0) { return 0;
memset(buf, 0, to_read << 2);
ready = to_read;
} }
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); SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
if (enable) { switch (cmd) {
case VOICE_ENABLE:
if (in->active) { if (in->active) {
return; break;
} }
in->active = 1; in->active = 1;
audio_rate_start(&in->rate); rate_start (&in->rate);
spice_server_record_start (&in->sin); spice_server_record_start (&in->sin);
} else { break;
case VOICE_DISABLE:
if (!in->active) { if (!in->active) {
return; break;
} }
in->active = 0; in->active = 0;
spice_server_record_stop (&in->sin); spice_server_record_stop (&in->sin);
} break;
} case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
static void line_in_volume(HWVoiceIn *hw, Volume *vol) SWVoiceIn *sw;
{ va_list ap;
SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw); uint16_t vol[2];
uint16_t svol[2];
assert(vol->channels == 2); va_start (ap, cmd);
svol[0] = vol->vol[0] * 257; sw = va_arg (ap, SWVoiceIn *);
svol[1] = vol->vol[1] * 257; va_end (ap);
spice_server_record_set_volume(&in->sin, 2, svol);
spice_server_record_set_mute(&in->sin, vol->mute); 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 #endif
break;
}
}
return 0;
}
static struct audio_option audio_options[] = {
{ /* end of list */ },
};
static struct audio_pcm_ops audio_callbacks = { static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init, .init_out = line_out_init,
.fini_out = line_out_fini, .fini_out = line_out_fini,
.write = audio_generic_write, .run_out = line_out_run,
.get_buffer_out = line_out_get_buffer, .write = line_out_write,
.put_buffer_out = line_out_put_buffer, .ctl_out = line_out_ctl,
.enable_out = line_out_enable,
#if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \
(SPICE_INTERFACE_PLAYBACK_MINOR >= 2)
.volume_out = line_out_volume,
#endif
.init_in = line_in_init, .init_in = line_in_init,
.fini_in = line_in_fini, .fini_in = line_in_fini,
.run_in = line_in_run,
.read = line_in_read, .read = line_in_read,
.enable_in = line_in_enable, .ctl_in = line_in_ctl,
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
.volume_in = line_in_volume,
#endif
}; };
static struct audio_driver spice_audio_driver = { static struct audio_driver spice_audio_driver = {
.name = "spice", .name = "spice",
.descr = "spice audio driver", .descr = "spice audio driver",
.options = audio_options,
.init = spice_audio_init, .init = spice_audio_init,
.fini = spice_audio_fini, .fini = spice_audio_fini,
.pcm_ops = &audio_callbacks, .pcm_ops = &audio_callbacks,
@@ -308,6 +402,9 @@ static struct audio_driver spice_audio_driver = {
.max_voices_in = 1, .max_voices_in = 1,
.voice_size_out = sizeof (SpiceVoiceOut), .voice_size_out = sizeof (SpiceVoiceOut),
.voice_size_in = sizeof (SpiceVoiceIn), .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) void qemu_spice_audio_init (void)

View File

@@ -1,6 +1,6 @@
# See docs/devel/tracing.txt for syntax documentation. # See docs/devel/tracing.txt for syntax documentation.
# alsaaudio.c # audio/alsaaudio.c
alsa_revents(int revents) "revents = %d" alsa_revents(int revents) "revents = %d"
alsa_pollout(int i, int fd) "i = %d fd = %d" alsa_pollout(int i, int fd) "i = %d fd = %d"
alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d" alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d"
@@ -12,11 +12,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
alsa_resume_in(void) "Resuming suspended input stream" alsa_resume_in(void) "Resuming suspended input stream"
alsa_no_frames(int state) "No frames available and ALSA state is %d" alsa_no_frames(int state) "No frames available and ALSA state is %d"
# ossaudio.c # audio/ossaudio.c
oss_version(int version) "OSS version = 0x%x" oss_version(int version) "OSS version = 0x%x"
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
# audio.c # audio/audio.c
audio_timer_start(int interval) "interval %d ms" audio_timer_start(int interval) "interval %d ms"
audio_timer_stop(void) "" audio_timer_stop(void) ""
audio_timer_delayed(int interval) "interval %d ms" audio_timer_delayed(int interval) "interval %d ms"

View File

@@ -21,12 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qapi/opts-visitor.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "wav" #define AUDIO_CAP "wav"
@@ -35,23 +32,63 @@
typedef struct WAVVoiceOut { typedef struct WAVVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
FILE *f; FILE *f;
RateCtl rate; int64_t old_ticks;
void *pcm_buf;
int total_samples; int total_samples;
} WAVVoiceOut; } WAVVoiceOut;
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) typedef struct {
struct audsettings settings;
const char *wav_path;
} WAVConf;
static int wav_run_out (HWVoiceOut *hw, int live)
{ {
WAVVoiceOut *wav = (WAVVoiceOut *) hw; WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len); int rpos, decr, samples;
assert(bytes % hw->info.bytes_per_frame == 0); 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) { if (bytes > INT_MAX) {
dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n", samples = INT_MAX >> hw->info.shift;
bytes, strerror(errno)); }
else {
samples = bytes >> hw->info.shift;
} }
wav->total_samples += bytes / hw->info.bytes_per_frame; wav->old_ticks = now;
return bytes; 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. */ /* VICE code: Store number as little endian. */
@@ -75,30 +112,25 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
}; };
Audiodev *dev = drv_opaque; WAVConf *conf = drv_opaque;
AudiodevWavOptions *wopts = &dev->u.wav; struct audsettings wav_as = conf->settings;
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
stereo = wav_as.nchannels == 2; stereo = wav_as.nchannels == 2;
switch (wav_as.fmt) { switch (wav_as.fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
bits16 = 0; bits16 = 0;
break; break;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
bits16 = 1; bits16 = 1;
break; break;
case AUDIO_FORMAT_S32: case AUD_FMT_S32:
case AUDIO_FORMAT_U32: case AUD_FMT_U32:
dolog ("WAVE files can not handle 32bit formats\n"); dolog ("WAVE files can not handle 32bit formats\n");
return -1; return -1;
default:
abort();
} }
hdr[34] = bits16 ? 0x10 : 0x08; hdr[34] = bits16 ? 0x10 : 0x08;
@@ -107,15 +139,24 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &wav_as); audio_pcm_init_info (&hw->info, &wav_as);
hw->samples = 1024; 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 + 22, hw->info.nchannels, 2);
le_store (hdr + 24, hw->info.freq, 4); le_store (hdr + 24, hw->info.freq, 4);
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2); le_store (hdr + 32, 1 << (bits16 + stereo), 2);
wav->f = fopen(wav_path, "wb"); wav->f = fopen (conf->wav_path, "wb");
if (!wav->f) { if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n", dolog ("Failed to open wave file `%s'\nReason: %s\n",
wav_path, strerror(errno)); conf->wav_path, strerror (errno));
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1; return -1;
} }
@@ -124,8 +165,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
strerror(errno)); strerror(errno));
return -1; return -1;
} }
audio_rate_start(&wav->rate);
return 0; return 0;
} }
@@ -134,7 +173,7 @@ static void wav_fini_out (HWVoiceOut *hw)
WAVVoiceOut *wav = (WAVVoiceOut *) hw; WAVVoiceOut *wav = (WAVVoiceOut *) hw;
uint8_t rlen[4]; uint8_t rlen[4];
uint8_t dlen[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; uint32_t rifflen = datalen + 36;
if (!wav->f) { if (!wav->f) {
@@ -171,39 +210,78 @@ static void wav_fini_out (HWVoiceOut *hw)
wav->f, strerror (errno)); wav->f, strerror (errno));
} }
wav->f = NULL; 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; (void) hw;
(void) cmd;
if (enable) { return 0;
audio_rate_start(&wav->rate);
}
} }
static void *wav_audio_init(Audiodev *dev) static WAVConf glob_conf = {
.settings.freq = 44100,
.settings.nchannels = 2,
.settings.fmt = AUD_FMT_S16,
.wav_path = "qemu.wav"
};
static void *wav_audio_init (void)
{ {
assert(dev->driver == AUDIODEV_DRIVER_WAV); WAVConf *conf = g_malloc(sizeof(WAVConf));
return dev; *conf = glob_conf;
return conf;
} }
static void wav_audio_fini (void *opaque) static void wav_audio_fini (void *opaque)
{ {
ldebug ("wav_fini"); ldebug ("wav_fini");
g_free(opaque);
} }
static struct audio_option wav_options[] = {
{
.name = "FREQUENCY",
.tag = AUD_OPT_INT,
.valp = &glob_conf.settings.freq,
.descr = "Frequency"
},
{
.name = "FORMAT",
.tag = AUD_OPT_FMT,
.valp = &glob_conf.settings.fmt,
.descr = "Format"
},
{
.name = "DAC_FIXED_CHANNELS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.settings.nchannels,
.descr = "Number of channels (1 - mono, 2 - stereo)"
},
{
.name = "PATH",
.tag = AUD_OPT_STR,
.valp = &glob_conf.wav_path,
.descr = "Path to wave file"
},
{ /* End of list */ }
};
static struct audio_pcm_ops wav_pcm_ops = { static struct audio_pcm_ops wav_pcm_ops = {
.init_out = wav_init_out, .init_out = wav_init_out,
.fini_out = wav_fini_out, .fini_out = wav_fini_out,
.run_out = wav_run_out,
.write = wav_write_out, .write = wav_write_out,
.run_buffer_out = audio_generic_run_buffer_out, .ctl_out = wav_ctl_out,
.enable_out = wav_enable_out,
}; };
static struct audio_driver wav_audio_driver = { static struct audio_driver wav_audio_driver = {
.name = "wav", .name = "wav",
.descr = "WAV renderer http://wikipedia.org/wiki/WAV", .descr = "WAV renderer http://wikipedia.org/wiki/WAV",
.options = wav_options,
.init = wav_audio_init, .init = wav_audio_init,
.fini = wav_audio_fini, .fini = wav_audio_fini,
.pcm_ops = &wav_pcm_ops, .pcm_ops = &wav_pcm_ops,

View File

@@ -1,4 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h"
#include "monitor/monitor.h" #include "monitor/monitor.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
@@ -71,7 +72,7 @@ static void wav_destroy (void *opaque)
g_free (wav->path); 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; WAVState *wav = opaque;
@@ -104,8 +105,8 @@ static struct capture_ops wav_capture_ops = {
.info = wav_capture_info .info = wav_capture_info
}; };
int wav_start_capture(AudioState *state, CaptureState *s, const char *path, int wav_start_capture (CaptureState *s, const char *path, int freq,
int freq, int bits, int nchannels) int bits, int nchannels)
{ {
WAVState *wav; WAVState *wav;
uint8_t hdr[] = { uint8_t hdr[] = {
@@ -135,7 +136,7 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
as.freq = freq; as.freq = freq;
as.nchannels = 1 << stereo; as.nchannels = 1 << stereo;
as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
as.endianness = 0; as.endianness = 0;
ops.notify = wav_notify; ops.notify = wav_notify;
@@ -170,7 +171,7 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
goto error_free; goto error_free;
} }
cap = AUD_add_capture(state, &as, &ops, wav); cap = AUD_add_capture (&as, &ops, wav);
if (!cap) { if (!cap) {
error_report("Failed to add audio capture"); error_report("Failed to add audio capture");
goto error_free; goto error_free;

View File

@@ -20,8 +20,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "authz/base.h" #include "authz/base.h"
#include "qemu/module.h" #include "authz/trace.h"
#include "trace.h"
bool qauthz_is_allowed(QAuthZ *authz, bool qauthz_is_allowed(QAuthZ *authz,
const char *identity, const char *identity,

View File

@@ -20,10 +20,9 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "authz/list.h" #include "authz/list.h"
#include "trace.h" #include "authz/trace.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qapi/qapi-visit-authz.h" #include "qapi/qapi-visit-authz.h"
#include "qemu/module.h"
static bool qauthz_list_is_allowed(QAuthZ *authz, static bool qauthz_list_is_allowed(QAuthZ *authz,
const char *identity, const char *identity,
@@ -124,12 +123,13 @@ qauthz_list_class_init(ObjectClass *oc, void *data)
"QAuthZListPolicy", "QAuthZListPolicy",
&QAuthZListPolicy_lookup, &QAuthZListPolicy_lookup,
qauthz_list_prop_get_policy, qauthz_list_prop_get_policy,
qauthz_list_prop_set_policy); qauthz_list_prop_set_policy,
NULL);
object_class_property_add(oc, "rules", "QAuthZListRule", object_class_property_add(oc, "rules", "QAuthZListRule",
qauthz_list_prop_get_rules, qauthz_list_prop_get_rules,
qauthz_list_prop_set_rules, qauthz_list_prop_set_rules,
NULL, NULL); NULL, NULL, NULL);
authz->is_allowed = qauthz_list_is_allowed; authz->is_allowed = qauthz_list_is_allowed;
} }

View File

@@ -20,10 +20,9 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "authz/listfile.h" #include "authz/listfile.h"
#include "trace.h" #include "authz/trace.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "qemu/filemonitor.h" #include "qemu/filemonitor.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
@@ -94,7 +93,7 @@ qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
static void static void
qauthz_list_file_event(int64_t wd G_GNUC_UNUSED, qauthz_list_file_event(int wd G_GNUC_UNUSED,
QFileMonitorEvent ev G_GNUC_UNUSED, QFileMonitorEvent ev G_GNUC_UNUSED,
const char *name G_GNUC_UNUSED, const char *name G_GNUC_UNUSED,
void *opaque) void *opaque)
@@ -221,10 +220,12 @@ qauthz_list_file_class_init(ObjectClass *oc, void *data)
object_class_property_add_str(oc, "filename", object_class_property_add_str(oc, "filename",
qauthz_list_file_prop_get_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", object_class_property_add_bool(oc, "refresh",
qauthz_list_file_prop_get_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; authz->is_allowed = qauthz_list_file_is_allowed;
} }
@@ -237,7 +238,7 @@ qauthz_list_file_init(Object *obj)
authz->file_watch = -1; authz->file_watch = -1;
#ifdef CONFIG_INOTIFY1 #ifdef CONFIG_INOTIFY1
authz->refresh = true; authz->refresh = TRUE;
#endif #endif
} }

View File

@@ -20,8 +20,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "authz/pamacct.h" #include "authz/pamacct.h"
#include "trace.h" #include "authz/trace.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include <security/pam_appl.h> #include <security/pam_appl.h>
@@ -107,7 +106,8 @@ qauthz_pam_class_init(ObjectClass *oc, void *data)
object_class_property_add_str(oc, "service", object_class_property_add_str(oc, "service",
qauthz_pam_prop_get_service, qauthz_pam_prop_get_service,
qauthz_pam_prop_set_service); qauthz_pam_prop_set_service,
NULL);
} }

View File

@@ -20,8 +20,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "authz/simple.h" #include "authz/simple.h"
#include "trace.h" #include "authz/trace.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
static bool qauthz_simple_is_allowed(QAuthZ *authz, static bool qauthz_simple_is_allowed(QAuthZ *authz,
@@ -74,7 +73,8 @@ qauthz_simple_class_init(ObjectClass *oc, void *data)
object_class_property_add_str(oc, "identity", object_class_property_add_str(oc, "identity",
qauthz_simple_prop_get_identity, qauthz_simple_prop_get_identity,
qauthz_simple_prop_set_identity); qauthz_simple_prop_set_identity,
NULL);
} }

View File

@@ -1,18 +1,18 @@
# See docs/devel/tracing.txt for syntax documentation. # See docs/devel/tracing.txt for syntax documentation.
# base.c # authz/base.c
qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d" qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
# simple.c # auth/simple.c
qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s" qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s"
# list.c # auth/list.c
qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d" qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d"
qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d" qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d"
# listfile.c # auth/listfile.c
qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s" qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d" qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
# pamacct.c # auth/pam.c
qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s" qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"

View File

@@ -1 +0,0 @@
source tpm/Kconfig

View File

@@ -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_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-y += hostmem.o hostmem-ram.o
common-obj-$(CONFIG_POSIX) += hostmem-file.o common-obj-$(CONFIG_POSIX) += hostmem-file.o
@@ -9,15 +9,10 @@ common-obj-$(CONFIG_POSIX) += hostmem-file.o
common-obj-y += cryptodev.o common-obj-y += cryptodev.o
common-obj-y += cryptodev-builtin.o common-obj-y += cryptodev-builtin.o
ifeq ($(CONFIG_VIRTIO_CRYPTO),y) ifeq ($(CONFIG_VIRTIO),y)
common-obj-y += cryptodev-vhost.o common-obj-y += cryptodev-vhost.o
common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
cryptodev-vhost-user.o
endif endif
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
common-obj-$(CONFIG_LINUX) += hostmem-memfd.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)

View File

@@ -23,6 +23,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "sysemu/cryptodev.h" #include "sysemu/cryptodev.h"
#include "hw/boards.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "standard-headers/linux/virtio_crypto.h" #include "standard-headers/linux/virtio_crypto.h"
#include "crypto/cipher.h" #include "crypto/cipher.h"
@@ -282,7 +283,12 @@ static int cryptodev_builtin_sym_close_session(
CryptoDevBackendBuiltin *builtin = CryptoDevBackendBuiltin *builtin =
CRYPTODEV_BACKEND_BUILTIN(backend); 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); qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
g_free(builtin->sessions[session_id]); g_free(builtin->sessions[session_id]);
@@ -351,7 +357,8 @@ static void cryptodev_builtin_cleanup(
for (i = 0; i < MAX_NUM_SESSIONS; i++) { for (i = 0; i < MAX_NUM_SESSIONS; i++) {
if (builtin->sessions[i] != NULL) { if (builtin->sessions[i] != NULL) {
cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort); cryptodev_builtin_sym_close_session(
backend, i, 0, errp);
} }
} }

View File

@@ -22,6 +22,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/boards.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
@@ -46,7 +47,7 @@
typedef struct CryptoDevBackendVhostUser { typedef struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj; CryptoDevBackend parent_obj;
VhostUserState vhost_user; VhostUserState *vhost_user;
CharBackend chr; CharBackend chr;
char *chr_name; char *chr_name;
bool opened; bool opened;
@@ -103,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
continue; continue;
} }
options.opaque = &s->vhost_user; options.opaque = s->vhost_user;
options.backend_type = VHOST_BACKEND_TYPE_USER; options.backend_type = VHOST_BACKEND_TYPE_USER;
options.cc = b->conf.peers.ccs[i]; options.cc = b->conf.peers.ccs[i];
s->vhost_crypto[i] = cryptodev_vhost_init(&options); s->vhost_crypto[i] = cryptodev_vhost_init(&options);
@@ -152,7 +153,7 @@ cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s,
return chr; 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; CryptoDevBackendVhostUser *s = opaque;
CryptoDevBackend *b = CRYPTODEV_BACKEND(s); CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
@@ -171,11 +172,6 @@ static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event)
b->ready = false; b->ready = false;
cryptodev_vhost_user_stop(queues, s); cryptodev_vhost_user_stop(queues, s);
break; break;
case CHR_EVENT_BREAK:
case CHR_EVENT_MUX_IN:
case CHR_EVENT_MUX_OUT:
/* Ignore */
break;
} }
} }
@@ -186,6 +182,7 @@ static void cryptodev_vhost_user_init(
size_t i; size_t i;
Error *local_err = NULL; Error *local_err = NULL;
Chardev *chr; Chardev *chr;
VhostUserState *user;
CryptoDevBackendClient *cc; CryptoDevBackendClient *cc;
CryptoDevBackendVhostUser *s = CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(backend); CRYPTODEV_BACKEND_VHOST_USER(backend);
@@ -209,16 +206,22 @@ static void cryptodev_vhost_user_init(
backend->conf.peers.ccs[i] = cc; backend->conf.peers.ccs[i] = cc;
if (i == 0) { 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; return;
} }
} }
} }
if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) { user = vhost_user_init();
if (!user) {
error_setg(errp, "Failed to init vhost_user");
return; return;
} }
user->chr = &s->chr;
s->vhost_user = user;
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
cryptodev_vhost_user_event, NULL, s, NULL, true); cryptodev_vhost_user_event, NULL, s, NULL, true);
@@ -304,7 +307,11 @@ static void cryptodev_vhost_user_cleanup(
} }
} }
vhost_user_cleanup(&s->vhost_user); if (s->vhost_user) {
vhost_user_cleanup(s->vhost_user);
g_free(s->vhost_user);
s->vhost_user = NULL;
}
} }
static void cryptodev_vhost_user_set_chardev(Object *obj, static void cryptodev_vhost_user_set_chardev(Object *obj,
@@ -339,7 +346,8 @@ static void cryptodev_vhost_user_instance_int(Object *obj)
{ {
object_property_add_str(obj, "chardev", object_property_add_str(obj, "chardev",
cryptodev_vhost_user_get_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) static void cryptodev_vhost_user_finalize(Object *obj)

View File

@@ -23,6 +23,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "sysemu/cryptodev.h" #include "sysemu/cryptodev.h"
#include "hw/boards.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "qemu/config-file.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) void *opaque, Error **errp)
{ {
CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj);
Error *local_err = NULL;
uint32_t value; uint32_t value;
if (!visit_type_uint32(v, name, &value, errp)) { visit_type_uint32(v, name, &value, &local_err);
return; if (local_err) {
goto out;
} }
if (!value) { if (!value) {
error_setg(errp, "Property '%s.%s' doesn't take value '%" PRIu32 "'", error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
object_get_typename(obj), name, value); PRIu32 "'", object_get_typename(obj), name, value);
return; goto out;
} }
backend->conf.peers.queues = value; backend->conf.peers.queues = value;
out:
error_propagate(errp, local_err);
} }
static void static void
@@ -172,10 +177,19 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp)
{ {
CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc); CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc);
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc); CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc);
Error *local_err = NULL;
if (bc->init) { 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) 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", object_property_add(obj, "queues", "uint32",
cryptodev_backend_get_queues, cryptodev_backend_get_queues,
cryptodev_backend_set_queues, cryptodev_backend_set_queues,
NULL, NULL); NULL, NULL, NULL);
/* Initialize devices' queues property to 1 */ /* 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) static void cryptodev_backend_finalize(Object *obj)

View File

@@ -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);

View File

@@ -9,15 +9,21 @@
* This work is licensed under the terms of the GNU GPL, version 2 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. * See the COPYING file in the top-level directory.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/module.h"
#include "sysemu/hostmem.h" #include "sysemu/hostmem.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "qom/object_interfaces.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) \ #define MEMORY_BACKEND_FILE(obj) \
OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE) OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE)
@@ -35,12 +41,10 @@ struct HostMemoryBackendFile {
static void static void
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{ {
#ifndef CONFIG_POSIX
error_setg(errp, "backend '%s' not supported on this host",
object_get_typename(OBJECT(backend)));
#else
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
#ifdef CONFIG_POSIX
gchar *name; gchar *name;
#endif
if (!backend->size) { if (!backend->size) {
error_setg(errp, "can't create backend with size 0"); error_setg(errp, "can't create backend with size 0");
@@ -50,7 +54,10 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
error_setg(errp, "mem-path property not set"); error_setg(errp, "mem-path property not set");
return; return;
} }
#ifndef CONFIG_POSIX
error_setg(errp, "-mem-path not supported on this host");
#else
backend->force_prealloc = mem_prealloc;
name = host_memory_backend_get_name(backend); name = host_memory_backend_get_name(backend);
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
name, name,
@@ -110,18 +117,23 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
{ {
HostMemoryBackend *backend = MEMORY_BACKEND(o); HostMemoryBackend *backend = MEMORY_BACKEND(o);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
Error *local_err = NULL;
uint64_t val; uint64_t val;
if (host_memory_backend_mr_inited(backend)) { if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property '%s' of %s", name, error_setg(&local_err, "cannot change property '%s' of %s",
object_get_typename(o)); name, object_get_typename(o));
return; goto out;
} }
if (!visit_type_size(v, name, &val, errp)) { visit_type_size(v, name, &val, &local_err);
return; if (local_err) {
goto out;
} }
fb->align = val; fb->align = val;
out:
error_propagate(errp, local_err);
} }
static bool file_memory_backend_get_pmem(Object *o, Error **errp) static bool file_memory_backend_get_pmem(Object *o, Error **errp)
@@ -135,6 +147,7 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
if (host_memory_backend_mr_inited(backend)) { if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property 'pmem' of %s.", error_setg(errp, "cannot change property 'pmem' of %s.",
object_get_typename(o)); object_get_typename(o));
return; return;
@@ -142,9 +155,13 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
#ifndef CONFIG_LIBPMEM #ifndef CONFIG_LIBPMEM
if (value) { 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.", " of %s. We can't ensure data persistence.",
object_get_typename(o)); object_get_typename(o));
error_propagate(errp, local_err);
return; return;
} }
#endif #endif
@@ -174,15 +191,18 @@ file_backend_class_init(ObjectClass *oc, void *data)
oc->unparent = file_backend_unparent; oc->unparent = file_backend_unparent;
object_class_property_add_bool(oc, "discard-data", 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", 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", object_class_property_add(oc, "align", "int",
file_memory_backend_get_align, file_memory_backend_get_align,
file_memory_backend_set_align, file_memory_backend_set_align,
NULL, NULL); NULL, NULL, &error_abort);
object_class_property_add_bool(oc, "pmem", 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) static void file_backend_instance_finalize(Object *o)

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